From 8a307d3601bcca99723b1a45e785adc3c9d3a476 Mon Sep 17 00:00:00 2001 From: Artur Świgoń Date: Thu, 21 May 2020 14:28:39 +0200 Subject: interconnect: Export of_icc_get_from_provider() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch makes the above function public (for use in exynos-bus devfreq driver). Signed-off-by: Artur Świgoń Reviewed-by: Krzysztof Kozlowski Reviewed-by: Chanwoo Choi Signed-off-by: Sylwester Nawrocki Link: https://lore.kernel.org/r/20200521122841.8867-2-s.nawrocki@samsung.com Signed-off-by: Georgi Djakov --- drivers/interconnect/core.c | 3 ++- include/linux/interconnect-provider.h | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/interconnect/core.c b/drivers/interconnect/core.c index e5f998744501..9e2d55d94fb4 100644 --- a/drivers/interconnect/core.c +++ b/drivers/interconnect/core.c @@ -334,7 +334,7 @@ EXPORT_SYMBOL_GPL(of_icc_xlate_onecell); * Returns a valid pointer to struct icc_node on success or ERR_PTR() * on failure. */ -static struct icc_node *of_icc_get_from_provider(struct of_phandle_args *spec) +struct icc_node *of_icc_get_from_provider(struct of_phandle_args *spec) { struct icc_node *node = ERR_PTR(-EPROBE_DEFER); struct icc_provider *provider; @@ -353,6 +353,7 @@ static struct icc_node *of_icc_get_from_provider(struct of_phandle_args *spec) return node; } +EXPORT_SYMBOL_GPL(of_icc_get_from_provider); static void devm_icc_release(struct device *dev, void *res) { diff --git a/include/linux/interconnect-provider.h b/include/linux/interconnect-provider.h index 0c494534b4d3..c92be2a90fa0 100644 --- a/include/linux/interconnect-provider.h +++ b/include/linux/interconnect-provider.h @@ -103,6 +103,7 @@ void icc_node_del(struct icc_node *node); int icc_nodes_remove(struct icc_provider *provider); int icc_provider_add(struct icc_provider *provider); int icc_provider_del(struct icc_provider *provider); +struct icc_node *of_icc_get_from_provider(struct of_phandle_args *spec); #else @@ -154,6 +155,11 @@ static inline int icc_provider_del(struct icc_provider *provider) return -ENOTSUPP; } +static inline struct icc_node *of_icc_get_from_provider(struct of_phandle_args *spec) +{ + return ERR_PTR(-ENOTSUPP); +} + #endif /* CONFIG_INTERCONNECT */ #endif /* __LINUX_INTERCONNECT_PROVIDER_H */ -- cgit v1.2.3-59-g8ed1b From 0259a41da3233744f6a3e7848430da13100423d6 Mon Sep 17 00:00:00 2001 From: Artur Świgoń Date: Thu, 21 May 2020 14:28:40 +0200 Subject: interconnect: Relax requirement in of_icc_get_from_provider() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch relaxes the condition in of_icc_get_from_provider() so that it is no longer required to set '#interconnect-cells' to <1> in the DT, and therefore it is not required to supply dummy node IDs in the 'interconnects' property when node IDs are dynamically generated rather than hardcoded (statically allocated). In case of the devfreq driver for exynos-bus, node IDs are dynamically allocated and '#interconnect-cells' is always zero. Signed-off-by: Artur Świgoń Acked-by: Krzysztof Kozlowski Reviewed-by: Chanwoo Choi Signed-off-by: Sylwester Nawrocki Link: https://lore.kernel.org/r/20200521122841.8867-3-s.nawrocki@samsung.com Signed-off-by: Georgi Djakov --- drivers/interconnect/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/interconnect/core.c b/drivers/interconnect/core.c index 9e2d55d94fb4..8b4d50d59e16 100644 --- a/drivers/interconnect/core.c +++ b/drivers/interconnect/core.c @@ -339,7 +339,7 @@ struct icc_node *of_icc_get_from_provider(struct of_phandle_args *spec) struct icc_node *node = ERR_PTR(-EPROBE_DEFER); struct icc_provider *provider; - if (!spec || spec->args_count != 1) + if (!spec) return ERR_PTR(-EINVAL); mutex_lock(&icc_lock); -- cgit v1.2.3-59-g8ed1b From 65461e26b1fe73bde4326367ee23cc1a24e6c33e Mon Sep 17 00:00:00 2001 From: Artur Świgoń Date: Thu, 21 May 2020 14:28:41 +0200 Subject: interconnect: Allow inter-provider pairs to be configured MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds support for a new boolean 'inter_set' field in struct icc_provider. Setting it to 'true' enables calling '->set' for inter-provider node pairs. All existing users of the interconnect framework allocate this structure with kzalloc, and are therefore unaffected by this change. This makes it easier for hierarchies like exynos-bus, where every bus is probed separately and registers a separate interconnect provider, to model constraints between buses. Signed-off-by: Artur Świgoń Signed-off-by: Sylwester Nawrocki Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20200521122841.8867-4-s.nawrocki@samsung.com Signed-off-by: Georgi Djakov --- drivers/interconnect/core.c | 11 +++++------ include/linux/interconnect-provider.h | 2 ++ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/interconnect/core.c b/drivers/interconnect/core.c index 8b4d50d59e16..609e206bf598 100644 --- a/drivers/interconnect/core.c +++ b/drivers/interconnect/core.c @@ -263,23 +263,22 @@ static int aggregate_requests(struct icc_node *node) static int apply_constraints(struct icc_path *path) { struct icc_node *next, *prev = NULL; + struct icc_provider *p; int ret = -EINVAL; int i; for (i = 0; i < path->num_nodes; i++) { next = path->reqs[i].node; + p = next->provider; - /* - * Both endpoints should be valid master-slave pairs of the - * same interconnect provider that will be configured. - */ - if (!prev || next->provider != prev->provider) { + /* both endpoints should be valid master-slave pairs */ + if (!prev || (p != prev->provider && !p->inter_set)) { prev = next; continue; } /* set the constraints */ - ret = next->provider->set(prev, next); + ret = p->set(prev, next); if (ret) goto out; diff --git a/include/linux/interconnect-provider.h b/include/linux/interconnect-provider.h index c92be2a90fa0..38701925ab91 100644 --- a/include/linux/interconnect-provider.h +++ b/include/linux/interconnect-provider.h @@ -41,6 +41,7 @@ struct icc_node *of_icc_xlate_onecell(struct of_phandle_args *spec, * @xlate: provider-specific callback for mapping nodes from phandle arguments * @dev: the device this interconnect provider belongs to * @users: count of active users + * @inter_set: whether inter-provider pairs will be configured with @set * @data: pointer to private data */ struct icc_provider { @@ -53,6 +54,7 @@ struct icc_provider { struct icc_node* (*xlate)(struct of_phandle_args *spec, void *data); struct device *dev; int users; + bool inter_set; void *data; }; -- cgit v1.2.3-59-g8ed1b From 12a400b016ab955be8e4c569346fa18aaceed9d7 Mon Sep 17 00:00:00 2001 From: Georgi Djakov Date: Tue, 16 Jun 2020 16:43:23 +0300 Subject: interconnect: Mark all dummy functions as static inline There are a few dummy stub functions that are not marked as static inline yet. Currently this header file is not included in any other file outside of drivers/interconnect/, but that might not be the case in the future. If this file gets included and the framework is disabled, we will be see warnings. Let's fix this in advance. Link: https://lore.kernel.org/r/20200228145945.13579-1-georgi.djakov@linaro.org Signed-off-by: Georgi Djakov --- include/linux/interconnect-provider.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/linux/interconnect-provider.h b/include/linux/interconnect-provider.h index 38701925ab91..4735518de515 100644 --- a/include/linux/interconnect-provider.h +++ b/include/linux/interconnect-provider.h @@ -120,7 +120,7 @@ static inline struct icc_node *icc_node_create(int id) return ERR_PTR(-ENOTSUPP); } -void icc_node_destroy(int id) +static inline void icc_node_destroy(int id) { } @@ -129,16 +129,16 @@ static inline int icc_link_create(struct icc_node *node, const int dst_id) return -ENOTSUPP; } -int icc_link_destroy(struct icc_node *src, struct icc_node *dst) +static inline int icc_link_destroy(struct icc_node *src, struct icc_node *dst) { return -ENOTSUPP; } -void icc_node_add(struct icc_node *node, struct icc_provider *provider) +static inline void icc_node_add(struct icc_node *node, struct icc_provider *provider) { } -void icc_node_del(struct icc_node *node) +static inline void icc_node_del(struct icc_node *node) { } -- cgit v1.2.3-59-g8ed1b From 55dc9b87e4a8e4d0fb1b7169bab7e23d27ba4209 Mon Sep 17 00:00:00 2001 From: John Hubbard Date: Mon, 25 May 2020 15:17:54 -0700 Subject: fpga: dfl: afu: convert get_user_pages() --> pin_user_pages() This code was using get_user_pages_fast(), in a "Case 2" scenario (DMA/RDMA), using the categorization from [1]. That means that it's time to convert the get_user_pages_fast() + put_page() calls to pin_user_pages_fast() + unpin_user_pages() calls. There is some helpful background in [2]: basically, this is a small part of fixing a long-standing disconnect between pinning pages, and file systems' use of those pages. [1] Documentation/core-api/pin_user_pages.rst [2] "Explicit pinning of user-space pages": https://lwn.net/Articles/807108/ Cc: Xu Yilun Cc: Wu Hao Cc: Moritz Fischer Cc: linux-fpga@vger.kernel.org Signed-off-by: John Hubbard Acked-by: Wu Hao Signed-off-by: Moritz Fischer --- drivers/fpga/dfl-afu-dma-region.c | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/drivers/fpga/dfl-afu-dma-region.c b/drivers/fpga/dfl-afu-dma-region.c index 02d8cbad1ae2..02b60fde0430 100644 --- a/drivers/fpga/dfl-afu-dma-region.c +++ b/drivers/fpga/dfl-afu-dma-region.c @@ -16,15 +16,6 @@ #include "dfl-afu.h" -static void put_all_pages(struct page **pages, int npages) -{ - int i; - - for (i = 0; i < npages; i++) - if (pages[i]) - put_page(pages[i]); -} - void afu_dma_region_init(struct dfl_feature_platform_data *pdata) { struct dfl_afu *afu = dfl_fpga_pdata_get_private(pdata); @@ -57,22 +48,22 @@ static int afu_dma_pin_pages(struct dfl_feature_platform_data *pdata, goto unlock_vm; } - pinned = get_user_pages_fast(region->user_addr, npages, FOLL_WRITE, + pinned = pin_user_pages_fast(region->user_addr, npages, FOLL_WRITE, region->pages); if (pinned < 0) { ret = pinned; goto free_pages; } else if (pinned != npages) { ret = -EFAULT; - goto put_pages; + goto unpin_pages; } dev_dbg(dev, "%d pages pinned\n", pinned); return 0; -put_pages: - put_all_pages(region->pages, pinned); +unpin_pages: + unpin_user_pages(region->pages, pinned); free_pages: kfree(region->pages); unlock_vm: @@ -94,7 +85,7 @@ static void afu_dma_unpin_pages(struct dfl_feature_platform_data *pdata, long npages = region->length >> PAGE_SHIFT; struct device *dev = &pdata->dev->dev; - put_all_pages(region->pages, npages); + unpin_user_pages(region->pages, npages); kfree(region->pages); account_locked_vm(current->mm, npages, false); -- cgit v1.2.3-59-g8ed1b From 9d08b9ecbb2c7e0efa124e9a67715336ec0dd174 Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Thu, 11 Jun 2020 23:11:40 +0200 Subject: dt-bindings: fpga: xilinx-slave-serial: valid for the 7 Series too The Xilinx 7-series uses the same protocol, mention that. Signed-off-by: Luca Ceresoli Acked-by: Moritz Fischer Acked-by: Rob Herring Signed-off-by: Moritz Fischer --- Documentation/devicetree/bindings/fpga/xilinx-slave-serial.txt | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/fpga/xilinx-slave-serial.txt b/Documentation/devicetree/bindings/fpga/xilinx-slave-serial.txt index cfa4ed42b62f..9f103f3872e8 100644 --- a/Documentation/devicetree/bindings/fpga/xilinx-slave-serial.txt +++ b/Documentation/devicetree/bindings/fpga/xilinx-slave-serial.txt @@ -1,11 +1,14 @@ Xilinx Slave Serial SPI FPGA Manager -Xilinx Spartan-6 FPGAs support a method of loading the bitstream over -what is referred to as "slave serial" interface. +Xilinx Spartan-6 and 7 Series FPGAs support a method of loading the +bitstream over what is referred to as "slave serial" interface. The slave serial link is not technically SPI, and might require extra circuits in order to play nicely with other SPI slaves on the same bus. -See https://www.xilinx.com/support/documentation/user_guides/ug380.pdf +See: +- https://www.xilinx.com/support/documentation/user_guides/ug380.pdf +- https://www.xilinx.com/support/documentation/user_guides/ug470_7Series_Config.pdf +- https://www.xilinx.com/support/documentation/application_notes/xapp583-fpga-configuration.pdf Required properties: - compatible: should contain "xlnx,fpga-slave-serial" -- cgit v1.2.3-59-g8ed1b From d1ddca78f9f603ab70369e76fa2610d7e385c1b8 Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Thu, 11 Jun 2020 23:11:41 +0200 Subject: fpga manager: xilinx-spi: valid for the 7 Series too The Xilinx 7-series uses the same protocol, mention that. Signed-off-by: Luca Ceresoli Signed-off-by: Moritz Fischer --- drivers/fpga/xilinx-spi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/fpga/xilinx-spi.c b/drivers/fpga/xilinx-spi.c index 272ee0c22822..79106626c3f8 100644 --- a/drivers/fpga/xilinx-spi.c +++ b/drivers/fpga/xilinx-spi.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Xilinx Spartan6 Slave Serial SPI Driver + * Xilinx Spartan6 and 7 Series Slave Serial SPI Driver * * Copyright (C) 2017 DENX Software Engineering * -- cgit v1.2.3-59-g8ed1b From 23f872b6ece695750667a44016037154b91addef Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Thu, 11 Jun 2020 23:11:42 +0200 Subject: fpga manager: xilinx-spi: remove unneeded, mistyped variables Using variables does not add readability here: parameters passed to udelay*() are obviously in microseconds and their meaning is clear from the context. The type is also wrong, udelay expects an unsigned long. Signed-off-by: Luca Ceresoli Signed-off-by: Moritz Fischer --- drivers/fpga/xilinx-spi.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/fpga/xilinx-spi.c b/drivers/fpga/xilinx-spi.c index 79106626c3f8..799ae04301be 100644 --- a/drivers/fpga/xilinx-spi.c +++ b/drivers/fpga/xilinx-spi.c @@ -41,8 +41,6 @@ static int xilinx_spi_write_init(struct fpga_manager *mgr, const char *buf, size_t count) { struct xilinx_spi_conf *conf = mgr->priv; - const size_t prog_latency_7500us = 7500; - const size_t prog_pulse_1us = 1; if (info->flags & FPGA_MGR_PARTIAL_RECONFIG) { dev_err(&mgr->dev, "Partial reconfiguration not supported.\n"); @@ -51,7 +49,7 @@ static int xilinx_spi_write_init(struct fpga_manager *mgr, gpiod_set_value(conf->prog_b, 1); - udelay(prog_pulse_1us); /* min is 500 ns */ + udelay(1); /* min is 500 ns */ gpiod_set_value(conf->prog_b, 0); @@ -61,7 +59,7 @@ static int xilinx_spi_write_init(struct fpga_manager *mgr, } /* program latency */ - usleep_range(prog_latency_7500us, prog_latency_7500us + 100); + usleep_range(7500, 7600); return 0; } -- cgit v1.2.3-59-g8ed1b From e1d9ec3af3463c1721723f68403ee14214d0d2f6 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Wed, 17 Jun 2020 17:10:39 -0500 Subject: fpga: dfl: Use struct_size() in kzalloc() Make use of the struct_size() helper instead of an open-coded version in order to avoid any potential type mistakes. Also, remove unnecessary function dfl_feature_platform_data_size(). This code was detected with the help of Coccinelle and, audited and fixed manually. Signed-off-by: Gustavo A. R. Silva Signed-off-by: Moritz Fischer --- drivers/fpga/dfl.c | 3 +-- drivers/fpga/dfl.h | 6 ------ 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/drivers/fpga/dfl.c b/drivers/fpga/dfl.c index 990994874bf1..2dd13e036d45 100644 --- a/drivers/fpga/dfl.c +++ b/drivers/fpga/dfl.c @@ -487,8 +487,7 @@ static int build_info_commit_dev(struct build_feature_devs_info *binfo) * it will be automatically freed by device's release() callback, * platform_device_release(). */ - pdata = kzalloc(dfl_feature_platform_data_size(binfo->feature_num), - GFP_KERNEL); + pdata = kzalloc(struct_size(pdata, features, binfo->feature_num), GFP_KERNEL); if (!pdata) return -ENOMEM; diff --git a/drivers/fpga/dfl.h b/drivers/fpga/dfl.h index 2f5d3052e36e..044b0e88e5a8 100644 --- a/drivers/fpga/dfl.h +++ b/drivers/fpga/dfl.h @@ -299,12 +299,6 @@ struct dfl_feature_ops { #define DFL_FPGA_FEATURE_DEV_FME "dfl-fme" #define DFL_FPGA_FEATURE_DEV_PORT "dfl-port" -static inline int dfl_feature_platform_data_size(const int num) -{ - return sizeof(struct dfl_feature_platform_data) + - num * sizeof(struct dfl_feature); -} - void dfl_fpga_dev_feature_uinit(struct platform_device *pdev); int dfl_fpga_dev_feature_init(struct platform_device *pdev, struct dfl_feature_driver *feature_drvs); -- cgit v1.2.3-59-g8ed1b From 88aaab9218f87c7c32e5db93554cf110352c3c13 Mon Sep 17 00:00:00 2001 From: Tom Rix Date: Mon, 8 Jun 2020 05:54:45 -0700 Subject: fpga: Fix dead store fpga-mgr.c Using clang's scan-build/view this issue was flagged in fpga-mgr.c drivers/fpga/fpga-mgr.c:585:3: warning: Value stored to 'ret' is never read [deadcode.DeadStores] ret = id; Signed-off-by: Tom Rix Signed-off-by: Moritz Fischer --- drivers/fpga/fpga-mgr.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/fpga/fpga-mgr.c b/drivers/fpga/fpga-mgr.c index e05104f5e40c..f38bab01432e 100644 --- a/drivers/fpga/fpga-mgr.c +++ b/drivers/fpga/fpga-mgr.c @@ -581,10 +581,8 @@ struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name, return NULL; id = ida_simple_get(&fpga_mgr_ida, 0, 0, GFP_KERNEL); - if (id < 0) { - ret = id; + if (id < 0) goto error_kfree; - } mutex_init(&mgr->ref_mutex); -- cgit v1.2.3-59-g8ed1b From d3fbd739fc8a3cf9418c4a17ba7b2b5be24a3b2c Mon Sep 17 00:00:00 2001 From: Tom Rix Date: Mon, 8 Jun 2020 05:54:46 -0700 Subject: fpga: Fix dead store in fpga-bridge.c Using clang's scan-build/view this issue was flagged a dead store issue in fpga-bridge.c warning: Value stored to 'ret' is never read [deadcode.DeadStores] ret = id; Signed-off-by: Tom Rix Signed-off-by: Moritz Fischer --- drivers/fpga/fpga-bridge.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/fpga/fpga-bridge.c b/drivers/fpga/fpga-bridge.c index 4bab9028940a..2deccacc3aa7 100644 --- a/drivers/fpga/fpga-bridge.c +++ b/drivers/fpga/fpga-bridge.c @@ -328,7 +328,7 @@ struct fpga_bridge *fpga_bridge_create(struct device *dev, const char *name, void *priv) { struct fpga_bridge *bridge; - int id, ret = 0; + int id, ret; if (!name || !strlen(name)) { dev_err(dev, "Attempt to register with no name!\n"); @@ -340,10 +340,8 @@ struct fpga_bridge *fpga_bridge_create(struct device *dev, const char *name, return NULL; id = ida_simple_get(&fpga_bridge_ida, 0, 0, GFP_KERNEL); - if (id < 0) { - ret = id; + if (id < 0) goto error_kfree; - } mutex_init(&bridge->mutex); INIT_LIST_HEAD(&bridge->node); -- cgit v1.2.3-59-g8ed1b From 908442aa67197bce1d76a6c25165bc56e2f55534 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Tue, 16 Jun 2020 21:51:40 +0530 Subject: soundwire: Replace 'objs' by 'y' `-objs` is designed for building host programs, change to `-y`, more straightforward for device drivers. See Documentation/kbuild/makefiles.rst Signed-off-by: Vinod Koul Link: https://lore.kernel.org/r/20200616162140.2563535-1-vkoul@kernel.org Signed-off-by: Vinod Koul --- drivers/soundwire/Makefile | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/soundwire/Makefile b/drivers/soundwire/Makefile index b5871612613b..7c53ffae9f50 100644 --- a/drivers/soundwire/Makefile +++ b/drivers/soundwire/Makefile @@ -4,22 +4,22 @@ # #Bus Objs -soundwire-bus-objs := bus_type.o bus.o master.o slave.o mipi_disco.o stream.o \ +soundwire-bus-y := bus_type.o bus.o master.o slave.o mipi_disco.o stream.o \ sysfs_slave.o sysfs_slave_dpn.o obj-$(CONFIG_SOUNDWIRE) += soundwire-bus.o ifdef CONFIG_DEBUG_FS -soundwire-bus-objs += debugfs.o +soundwire-bus-y += debugfs.o endif #Cadence Objs -soundwire-cadence-objs := cadence_master.o +soundwire-cadence-y := cadence_master.o obj-$(CONFIG_SOUNDWIRE_CADENCE) += soundwire-cadence.o #Intel driver -soundwire-intel-objs := intel.o intel_init.o +soundwire-intel-y := intel.o intel_init.o obj-$(CONFIG_SOUNDWIRE_INTEL) += soundwire-intel.o #Qualcomm driver -soundwire-qcom-objs := qcom.o +soundwire-qcom-y := qcom.o obj-$(CONFIG_SOUNDWIRE_QCOM) += soundwire-qcom.o -- cgit v1.2.3-59-g8ed1b From 929cfee314d155e61ad84a590c4080885afdb214 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Sun, 31 May 2020 23:18:06 +0800 Subject: soundwire: bus: clock_stop: don't deal with UNATTACHED Slave devices We don't need to do anything for the slave if it is unattached during clock stop prepare and exit sequences. Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20200531151806.25951-1-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/bus.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 24ba77226376..fcfba1c38267 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -863,13 +863,13 @@ int sdw_bus_prep_clk_stop(struct sdw_bus *bus) if (!slave->dev_num) continue; - /* Identify if Slave(s) are available on Bus */ - is_slave = true; - if (slave->status != SDW_SLAVE_ATTACHED && slave->status != SDW_SLAVE_ALERT) continue; + /* Identify if Slave(s) are available on Bus */ + is_slave = true; + slave_mode = sdw_get_clk_stop_mode(slave); slave->curr_clk_stop_mode = slave_mode; @@ -900,6 +900,10 @@ int sdw_bus_prep_clk_stop(struct sdw_bus *bus) return ret; } + /* Don't need to inform slaves if there is no slave attached */ + if (!is_slave) + return ret; + /* Inform slaves that prep is done */ list_for_each_entry(slave, &bus->slaves, node) { if (!slave->dev_num) @@ -985,13 +989,13 @@ int sdw_bus_exit_clk_stop(struct sdw_bus *bus) if (!slave->dev_num) continue; - /* Identify if Slave(s) are available on Bus */ - is_slave = true; - if (slave->status != SDW_SLAVE_ATTACHED && slave->status != SDW_SLAVE_ALERT) continue; + /* Identify if Slave(s) are available on Bus */ + is_slave = true; + mode = slave->curr_clk_stop_mode; if (mode == SDW_CLK_STOP_MODE1) { @@ -1016,6 +1020,13 @@ int sdw_bus_exit_clk_stop(struct sdw_bus *bus) if (is_slave && !simple_clk_stop) sdw_bus_wait_for_clk_prep_deprep(bus, SDW_BROADCAST_DEV_NUM); + /* + * Don't need to call slave callback function if there is no slave + * attached + */ + if (!is_slave) + return 0; + list_for_each_entry(slave, &bus->slaves, node) { if (!slave->dev_num) continue; -- cgit v1.2.3-59-g8ed1b From b6109dd6dc9fbc5dfae1106972d728eba0afd098 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 1 Jun 2020 02:20:57 +0800 Subject: soundwire: intel: cleanups for indirections/logs The code can be simplified a bit to have a more consistent use of 'dev' and 'bus', as well as move definitions around. This will help make the major changes in follow-up patches easier to review. Signed-off-by: Pierre-Louis Bossart Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20200531182102.27840-2-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/intel.c | 68 ++++++++++++++++++++---------------------- drivers/soundwire/intel.h | 11 +++++++ drivers/soundwire/intel_init.c | 1 + 3 files changed, 45 insertions(+), 35 deletions(-) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 4cfdd074e310..9b4737098299 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -92,23 +92,12 @@ #define SDW_ALH_STRMZCFG_DMAT GENMASK(7, 0) #define SDW_ALH_STRMZCFG_CHN GENMASK(19, 16) -#define SDW_INTEL_QUIRK_MASK_BUS_DISABLE BIT(1) - enum intel_pdi_type { INTEL_PDI_IN = 0, INTEL_PDI_OUT = 1, INTEL_PDI_BD = 2, }; -struct sdw_intel { - struct sdw_cdns cdns; - int instance; - struct sdw_intel_link_res *link_res; -#ifdef CONFIG_DEBUG_FS - struct dentry *debugfs; -#endif -}; - #define cdns_to_intel(_cdns) container_of(_cdns, struct sdw_intel, cdns) /* @@ -1083,41 +1072,47 @@ static int intel_init(struct sdw_intel *sdw) /* * probe and init */ -static int intel_probe(struct platform_device *pdev) +static int intel_master_probe(struct platform_device *pdev) { struct sdw_cdns_stream_config config; + struct device *dev = &pdev->dev; struct sdw_intel *sdw; + struct sdw_bus *bus; int ret; - sdw = devm_kzalloc(&pdev->dev, sizeof(*sdw), GFP_KERNEL); + sdw = devm_kzalloc(dev, sizeof(*sdw), GFP_KERNEL); if (!sdw) return -ENOMEM; + bus = &sdw->cdns.bus; + sdw->instance = pdev->id; - sdw->link_res = dev_get_platdata(&pdev->dev); - sdw->cdns.dev = &pdev->dev; + sdw->link_res = dev_get_platdata(dev); + sdw->cdns.dev = dev; sdw->cdns.registers = sdw->link_res->registers; sdw->cdns.instance = sdw->instance; sdw->cdns.msg_count = 0; - sdw->cdns.bus.link_id = pdev->id; + bus->link_id = pdev->id; sdw_cdns_probe(&sdw->cdns); /* Set property read ops */ sdw_intel_ops.read_prop = intel_prop_read; - sdw->cdns.bus.ops = &sdw_intel_ops; + bus->ops = &sdw_intel_ops; + /* set driver data, accessed by snd_soc_dai_get_drvdata() */ platform_set_drvdata(pdev, sdw); - ret = sdw_bus_master_add(&sdw->cdns.bus, &pdev->dev, pdev->dev.fwnode); + ret = sdw_bus_master_add(bus, dev, dev->fwnode); if (ret) { - dev_err(&pdev->dev, "sdw_bus_master_add fail: %d\n", ret); + dev_err(dev, "sdw_bus_master_add fail: %d\n", ret); return ret; } - if (sdw->cdns.bus.prop.hw_disabled) { - dev_info(&pdev->dev, "SoundWire master %d is disabled, ignoring\n", - sdw->cdns.bus.link_id); + if (bus->prop.hw_disabled) { + dev_info(dev, + "SoundWire master %d is disabled, will be ignored\n", + bus->link_id); return 0; } @@ -1139,28 +1134,28 @@ static int intel_probe(struct platform_device *pdev) sdw_cdns_irq, sdw_cdns_thread, IRQF_SHARED, KBUILD_MODNAME, &sdw->cdns); if (ret < 0) { - dev_err(sdw->cdns.dev, "unable to grab IRQ %d, disabling device\n", + dev_err(dev, "unable to grab IRQ %d, disabling device\n", sdw->link_res->irq); goto err_init; } ret = sdw_cdns_enable_interrupt(&sdw->cdns, true); if (ret < 0) { - dev_err(sdw->cdns.dev, "cannot enable interrupts\n"); + dev_err(dev, "cannot enable interrupts\n"); goto err_init; } ret = sdw_cdns_exit_reset(&sdw->cdns); if (ret < 0) { - dev_err(sdw->cdns.dev, "unable to exit bus reset sequence\n"); + dev_err(dev, "unable to exit bus reset sequence\n"); goto err_interrupt; } /* Register DAIs */ ret = intel_register_dai(sdw); if (ret) { - dev_err(sdw->cdns.dev, "DAI registration failed: %d\n", ret); - snd_soc_unregister_component(sdw->cdns.dev); + dev_err(dev, "DAI registration failed: %d\n", ret); + snd_soc_unregister_component(dev); goto err_interrupt; } @@ -1172,33 +1167,36 @@ err_interrupt: sdw_cdns_enable_interrupt(&sdw->cdns, false); free_irq(sdw->link_res->irq, sdw); err_init: - sdw_bus_master_delete(&sdw->cdns.bus); + sdw_bus_master_delete(bus); return ret; } -static int intel_remove(struct platform_device *pdev) +static int intel_master_remove(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct sdw_intel *sdw; + struct sdw_bus *bus; sdw = platform_get_drvdata(pdev); - if (!sdw->cdns.bus.prop.hw_disabled) { + bus = &sdw->cdns.bus; + + if (!bus->prop.hw_disabled) { intel_debugfs_exit(sdw); sdw_cdns_enable_interrupt(&sdw->cdns, false); free_irq(sdw->link_res->irq, sdw); - snd_soc_unregister_component(sdw->cdns.dev); + snd_soc_unregister_component(dev); } - sdw_bus_master_delete(&sdw->cdns.bus); + sdw_bus_master_delete(bus); return 0; } static struct platform_driver sdw_intel_drv = { - .probe = intel_probe, - .remove = intel_remove, + .probe = intel_master_probe, + .remove = intel_master_remove, .driver = { .name = "int-sdw", - }, }; diff --git a/drivers/soundwire/intel.h b/drivers/soundwire/intel.h index 38b7c125fb10..4bd045ae91be 100644 --- a/drivers/soundwire/intel.h +++ b/drivers/soundwire/intel.h @@ -27,4 +27,15 @@ struct sdw_intel_link_res { struct device *dev; }; +struct sdw_intel { + struct sdw_cdns cdns; + int instance; + struct sdw_intel_link_res *link_res; +#ifdef CONFIG_DEBUG_FS + struct dentry *debugfs; +#endif +}; + +#define SDW_INTEL_QUIRK_MASK_BUS_DISABLE BIT(1) + #endif /* __SDW_INTEL_LOCAL_H */ diff --git a/drivers/soundwire/intel_init.c b/drivers/soundwire/intel_init.c index d5d42795a48f..188a1fa17e27 100644 --- a/drivers/soundwire/intel_init.c +++ b/drivers/soundwire/intel_init.c @@ -13,6 +13,7 @@ #include #include #include +#include "cadence_master.h" #include "intel.h" #define SDW_LINK_TYPE 4 /* from Intel ACPI documentation */ -- cgit v1.2.3-59-g8ed1b From 83e129afbe5c4a444c034e981b0f8535889380fe Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 1 Jun 2020 02:20:58 +0800 Subject: soundwire: intel: clarify drvdata and remove more indirections The use of drvdata mixes two structures. There was no harm the first structure is embedded as the first element of the second, but that's not good. Make sure all drvdata is based on the 'sdw_cdns' structure. While we are at it, remove indirections for 'dev' and 'cdns' to make the code more readable. Signed-off-by: Pierre-Louis Bossart Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20200531182102.27840-3-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/intel.c | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 9b4737098299..5053e176a6f3 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -1077,6 +1077,7 @@ static int intel_master_probe(struct platform_device *pdev) struct sdw_cdns_stream_config config; struct device *dev = &pdev->dev; struct sdw_intel *sdw; + struct sdw_cdns *cdns; struct sdw_bus *bus; int ret; @@ -1084,24 +1085,26 @@ static int intel_master_probe(struct platform_device *pdev) if (!sdw) return -ENOMEM; - bus = &sdw->cdns.bus; + cdns = &sdw->cdns; + bus = &cdns->bus; sdw->instance = pdev->id; sdw->link_res = dev_get_platdata(dev); - sdw->cdns.dev = dev; - sdw->cdns.registers = sdw->link_res->registers; - sdw->cdns.instance = sdw->instance; - sdw->cdns.msg_count = 0; + cdns->dev = dev; + cdns->registers = sdw->link_res->registers; + cdns->instance = sdw->instance; + cdns->msg_count = 0; + bus->link_id = pdev->id; - sdw_cdns_probe(&sdw->cdns); + sdw_cdns_probe(cdns); /* Set property read ops */ sdw_intel_ops.read_prop = intel_prop_read; bus->ops = &sdw_intel_ops; /* set driver data, accessed by snd_soc_dai_get_drvdata() */ - platform_set_drvdata(pdev, sdw); + dev_set_drvdata(dev, cdns); ret = sdw_bus_master_add(bus, dev, dev->fwnode); if (ret) { @@ -1123,7 +1126,7 @@ static int intel_master_probe(struct platform_device *pdev) /* Read the PDI config and initialize cadence PDI */ intel_pdi_init(sdw, &config); - ret = sdw_cdns_pdi_init(&sdw->cdns, config); + ret = sdw_cdns_pdi_init(cdns, config); if (ret) goto err_init; @@ -1132,20 +1135,20 @@ static int intel_master_probe(struct platform_device *pdev) /* Acquire IRQ */ ret = request_threaded_irq(sdw->link_res->irq, sdw_cdns_irq, sdw_cdns_thread, - IRQF_SHARED, KBUILD_MODNAME, &sdw->cdns); + IRQF_SHARED, KBUILD_MODNAME, cdns); if (ret < 0) { dev_err(dev, "unable to grab IRQ %d, disabling device\n", sdw->link_res->irq); goto err_init; } - ret = sdw_cdns_enable_interrupt(&sdw->cdns, true); + ret = sdw_cdns_enable_interrupt(cdns, true); if (ret < 0) { dev_err(dev, "cannot enable interrupts\n"); goto err_init; } - ret = sdw_cdns_exit_reset(&sdw->cdns); + ret = sdw_cdns_exit_reset(cdns); if (ret < 0) { dev_err(dev, "unable to exit bus reset sequence\n"); goto err_interrupt; @@ -1164,7 +1167,7 @@ static int intel_master_probe(struct platform_device *pdev) return 0; err_interrupt: - sdw_cdns_enable_interrupt(&sdw->cdns, false); + sdw_cdns_enable_interrupt(cdns, false); free_irq(sdw->link_res->irq, sdw); err_init: sdw_bus_master_delete(bus); @@ -1174,16 +1177,13 @@ err_init: static int intel_master_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct sdw_intel *sdw; - struct sdw_bus *bus; - - sdw = platform_get_drvdata(pdev); - - bus = &sdw->cdns.bus; + struct sdw_cdns *cdns = dev_get_drvdata(dev); + struct sdw_intel *sdw = cdns_to_intel(cdns); + struct sdw_bus *bus = &cdns->bus; if (!bus->prop.hw_disabled) { intel_debugfs_exit(sdw); - sdw_cdns_enable_interrupt(&sdw->cdns, false); + sdw_cdns_enable_interrupt(cdns, false); free_irq(sdw->link_res->irq, sdw); snd_soc_unregister_component(dev); } -- cgit v1.2.3-59-g8ed1b From 9cd1c5a721d2e1d767d9add0ebf53c93459ba87c Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 1 Jun 2020 02:20:59 +0800 Subject: soundwire: intel_init: remove useless test No need to test link_mask twice Suggested-by: Rander Wang Signed-off-by: Pierre-Louis Bossart Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20200531182102.27840-4-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/intel_init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soundwire/intel_init.c b/drivers/soundwire/intel_init.c index 188a1fa17e27..d6e85e582c9b 100644 --- a/drivers/soundwire/intel_init.c +++ b/drivers/soundwire/intel_init.c @@ -109,7 +109,7 @@ static struct sdw_intel_ctx /* Create SDW Master devices */ for (i = 0; i < count; i++) { - if (link_mask && !(link_mask & BIT(i))) { + if (!(link_mask & BIT(i))) { dev_dbg(&adev->dev, "Link %d masked, will not be enabled\n", i); link++; -- cgit v1.2.3-59-g8ed1b From dd906cc615a5c0f7b95c20200963958c1cc0ea74 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 1 Jun 2020 02:21:00 +0800 Subject: soundwire: intel_init: use devm_ allocation Make error handling simpler with devm_ allocation. Signed-off-by: Pierre-Louis Bossart Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20200531182102.27840-5-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/intel_init.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/drivers/soundwire/intel_init.c b/drivers/soundwire/intel_init.c index d6e85e582c9b..57eac5fd4840 100644 --- a/drivers/soundwire/intel_init.c +++ b/drivers/soundwire/intel_init.c @@ -42,9 +42,6 @@ static int sdw_intel_cleanup_pdev(struct sdw_intel_ctx *ctx) link++; } - kfree(ctx->links); - ctx->links = NULL; - return 0; } @@ -96,14 +93,15 @@ static struct sdw_intel_ctx dev_dbg(&adev->dev, "Creating %d SDW Link devices\n", count); - ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + ctx = devm_kzalloc(&adev->dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) return NULL; ctx->count = count; - ctx->links = kcalloc(ctx->count, sizeof(*ctx->links), GFP_KERNEL); + ctx->links = devm_kcalloc(&adev->dev, ctx->count, + sizeof(*ctx->links), GFP_KERNEL); if (!ctx->links) - goto link_err; + return NULL; link = ctx->links; @@ -146,9 +144,8 @@ static struct sdw_intel_ctx return ctx; pdev_err: + ctx->count = i; sdw_intel_cleanup_pdev(ctx); -link_err: - kfree(ctx); return NULL; } @@ -216,7 +213,6 @@ void *sdw_intel_init(acpi_handle *parent_handle, struct sdw_intel_res *res) void sdw_intel_exit(struct sdw_intel_ctx *ctx) { sdw_intel_cleanup_pdev(ctx); - kfree(ctx); } EXPORT_SYMBOL(sdw_intel_exit); -- cgit v1.2.3-59-g8ed1b From 4ab34412fc62fba29ba00d76cf2c46585b3e5ba3 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 1 Jun 2020 02:21:01 +0800 Subject: soundwire: intel_init: pass link information as platform data It's not clear how this code ever worked, the link information is used in intel.c but never passed as platform_data. Signed-off-by: Pierre-Louis Bossart Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20200531182102.27840-6-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/intel_init.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/soundwire/intel_init.c b/drivers/soundwire/intel_init.c index 57eac5fd4840..65e3a24ea0ac 100644 --- a/drivers/soundwire/intel_init.c +++ b/drivers/soundwire/intel_init.c @@ -128,6 +128,8 @@ static struct sdw_intel_ctx pdevinfo.name = "int-sdw"; pdevinfo.id = i; pdevinfo.fwnode = acpi_fwnode_handle(adev); + pdevinfo.data = link; + pdevinfo.size_data = sizeof(*link); pdev = platform_device_register_full(&pdevinfo); if (IS_ERR(pdev)) { -- cgit v1.2.3-59-g8ed1b From 6d2c66695bf30355bacceb2b0635d3ddaf26cce4 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 1 Jun 2020 02:21:02 +0800 Subject: soundwire: intel: transition to 3 steps initialization Rather than a plain-vanilla init/exit, this patch provides 3 steps in the initialization needed for driver selection, machine driver selection and deal with power rail dependencies. - ACPI scan: this step is done at a very early stage to detect the presence of a SoundWire Controller and enabled links at the BIOS level. This step may be called from the legacy HDaudio driver, which will abort its probe to let the Sound Open Firmware (SOF) handle the hardware. - probe: this step allocates all the required memory and will add a sdw_bus, which in turn will result in identifying all possible Slaves listed below the Controller ACPI companion device. All the information is reported to the parent PCI driver which will select the relevant machine driver. - startup: this last step starts the bus reset, which results in Slave devices reporting as ATTACHED and being enumerated. This step is only done during the card creation stage, after the DSP is powered to account for internal power rail dependencies. These 3 steps are already supported in the Sound Open firmware drivers and upstream. Signed-off-by: Pierre-Louis Bossart Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20200531182102.27840-7-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/intel.c | 50 ++++++--- drivers/soundwire/intel.h | 2 + drivers/soundwire/intel_init.c | 242 +++++++++++++++++++++++++++++++++-------- 3 files changed, 232 insertions(+), 62 deletions(-) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 5053e176a6f3..950e4cf09f2a 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -1074,7 +1074,6 @@ static int intel_init(struct sdw_intel *sdw) */ static int intel_master_probe(struct platform_device *pdev) { - struct sdw_cdns_stream_config config; struct device *dev = &pdev->dev; struct sdw_intel *sdw; struct sdw_cdns *cdns; @@ -1112,10 +1111,41 @@ static int intel_master_probe(struct platform_device *pdev) return ret; } - if (bus->prop.hw_disabled) { + if (bus->prop.hw_disabled) dev_info(dev, "SoundWire master %d is disabled, will be ignored\n", bus->link_id); + + /* Acquire IRQ */ + ret = request_threaded_irq(sdw->link_res->irq, + sdw_cdns_irq, sdw_cdns_thread, + IRQF_SHARED, KBUILD_MODNAME, cdns); + if (ret < 0) { + dev_err(dev, "unable to grab IRQ %d, disabling device\n", + sdw->link_res->irq); + goto err_init; + } + + return 0; + +err_init: + sdw_bus_master_delete(bus); + return ret; +} + +int intel_master_startup(struct platform_device *pdev) +{ + struct sdw_cdns_stream_config config; + struct device *dev = &pdev->dev; + struct sdw_cdns *cdns = dev_get_drvdata(dev); + struct sdw_intel *sdw = cdns_to_intel(cdns); + struct sdw_bus *bus = &cdns->bus; + int ret; + + if (bus->prop.hw_disabled) { + dev_info(dev, + "SoundWire master %d is disabled, ignoring\n", + sdw->instance); return 0; } @@ -1132,16 +1162,6 @@ static int intel_master_probe(struct platform_device *pdev) intel_pdi_ch_update(sdw); - /* Acquire IRQ */ - ret = request_threaded_irq(sdw->link_res->irq, - sdw_cdns_irq, sdw_cdns_thread, - IRQF_SHARED, KBUILD_MODNAME, cdns); - if (ret < 0) { - dev_err(dev, "unable to grab IRQ %d, disabling device\n", - sdw->link_res->irq); - goto err_init; - } - ret = sdw_cdns_enable_interrupt(cdns, true); if (ret < 0) { dev_err(dev, "cannot enable interrupts\n"); @@ -1168,9 +1188,7 @@ static int intel_master_probe(struct platform_device *pdev) err_interrupt: sdw_cdns_enable_interrupt(cdns, false); - free_irq(sdw->link_res->irq, sdw); err_init: - sdw_bus_master_delete(bus); return ret; } @@ -1196,12 +1214,12 @@ static struct platform_driver sdw_intel_drv = { .probe = intel_master_probe, .remove = intel_master_remove, .driver = { - .name = "int-sdw", + .name = "intel-sdw", }, }; module_platform_driver(sdw_intel_drv); MODULE_LICENSE("Dual BSD/GPL"); -MODULE_ALIAS("platform:int-sdw"); +MODULE_ALIAS("platform:intel-sdw"); MODULE_DESCRIPTION("Intel Soundwire Master Driver"); diff --git a/drivers/soundwire/intel.h b/drivers/soundwire/intel.h index 4bd045ae91be..694117370ac3 100644 --- a/drivers/soundwire/intel.h +++ b/drivers/soundwire/intel.h @@ -38,4 +38,6 @@ struct sdw_intel { #define SDW_INTEL_QUIRK_MASK_BUS_DISABLE BIT(1) +int intel_master_startup(struct platform_device *pdev); + #endif /* __SDW_INTEL_LOCAL_H */ diff --git a/drivers/soundwire/intel_init.c b/drivers/soundwire/intel_init.c index 65e3a24ea0ac..3f2e884b4f6d 100644 --- a/drivers/soundwire/intel_init.c +++ b/drivers/soundwire/intel_init.c @@ -24,73 +24,146 @@ #define SDW_LINK_BASE 0x30000 #define SDW_LINK_SIZE 0x10000 -static int link_mask; -module_param_named(sdw_link_mask, link_mask, int, 0444); +static int ctrl_link_mask; +module_param_named(sdw_link_mask, ctrl_link_mask, int, 0444); MODULE_PARM_DESC(sdw_link_mask, "Intel link mask (one bit per link)"); -static int sdw_intel_cleanup_pdev(struct sdw_intel_ctx *ctx) +static bool is_link_enabled(struct fwnode_handle *fw_node, int i) +{ + struct fwnode_handle *link; + char name[32]; + u32 quirk_mask = 0; + + /* Find master handle */ + snprintf(name, sizeof(name), + "mipi-sdw-link-%d-subproperties", i); + + link = fwnode_get_named_child_node(fw_node, name); + if (!link) + return false; + + fwnode_property_read_u32(link, + "intel-quirk-mask", + &quirk_mask); + + if (quirk_mask & SDW_INTEL_QUIRK_MASK_BUS_DISABLE) + return false; + + return true; +} + +static int sdw_intel_cleanup(struct sdw_intel_ctx *ctx) { struct sdw_intel_link_res *link = ctx->links; + u32 link_mask; int i; if (!link) return 0; - for (i = 0; i < ctx->count; i++) { + link_mask = ctx->link_mask; + + for (i = 0; i < ctx->count; i++, link++) { + if (!(link_mask & BIT(i))) + continue; + if (link->pdev) platform_device_unregister(link->pdev); - link++; } return 0; } -static struct sdw_intel_ctx -*sdw_intel_add_controller(struct sdw_intel_res *res) +static int +sdw_intel_scan_controller(struct sdw_intel_acpi_info *info) { - struct platform_device_info pdevinfo; - struct platform_device *pdev; - struct sdw_intel_link_res *link; - struct sdw_intel_ctx *ctx; struct acpi_device *adev; int ret, i; u8 count; - u32 caps; - if (acpi_bus_get_device(res->handle, &adev)) - return NULL; + if (acpi_bus_get_device(info->handle, &adev)) + return -EINVAL; /* Found controller, find links supported */ count = 0; ret = fwnode_property_read_u8_array(acpi_fwnode_handle(adev), "mipi-sdw-master-count", &count, 1); - /* Don't fail on error, continue and use hw value */ + /* + * In theory we could check the number of links supported in + * hardware, but in that step we cannot assume SoundWire IP is + * powered. + * + * In addition, if the BIOS doesn't even provide this + * 'master-count' property then all the inits based on link + * masks will fail as well. + * + * We will check the hardware capabilities in the startup() step + */ + if (ret) { dev_err(&adev->dev, "Failed to read mipi-sdw-master-count: %d\n", ret); - count = SDW_MAX_LINKS; + return -EINVAL; } - /* Check SNDWLCAP.LCOUNT */ - caps = ioread32(res->mmio_base + SDW_SHIM_BASE + SDW_SHIM_LCAP); - caps &= GENMASK(2, 0); - - /* Check HW supported vs property value and use min of two */ - count = min_t(u8, caps, count); - /* Check count is within bounds */ if (count > SDW_MAX_LINKS) { dev_err(&adev->dev, "Link count %d exceeds max %d\n", count, SDW_MAX_LINKS); - return NULL; + return -EINVAL; } if (!count) { dev_warn(&adev->dev, "No SoundWire links detected\n"); - return NULL; + return -EINVAL; + } + dev_dbg(&adev->dev, "ACPI reports %d SDW Link devices\n", count); + + info->count = count; + info->link_mask = 0; + + for (i = 0; i < count; i++) { + if (ctrl_link_mask && !(ctrl_link_mask & BIT(i))) { + dev_dbg(&adev->dev, + "Link %d masked, will not be enabled\n", i); + continue; + } + + if (!is_link_enabled(acpi_fwnode_handle(adev), i)) { + dev_dbg(&adev->dev, + "Link %d not selected in firmware\n", i); + continue; + } + + info->link_mask |= BIT(i); } + return 0; +} + +static struct sdw_intel_ctx +*sdw_intel_probe_controller(struct sdw_intel_res *res) +{ + struct platform_device_info pdevinfo; + struct platform_device *pdev; + struct sdw_intel_link_res *link; + struct sdw_intel_ctx *ctx; + struct acpi_device *adev; + u32 link_mask; + int count; + int i; + + if (!res) + return NULL; + + if (acpi_bus_get_device(res->handle, &adev)) + return NULL; + + if (!res->count) + return NULL; + + count = res->count; dev_dbg(&adev->dev, "Creating %d SDW Link devices\n", count); ctx = devm_kzalloc(&adev->dev, sizeof(*ctx), GFP_KERNEL); @@ -103,19 +176,25 @@ static struct sdw_intel_ctx if (!ctx->links) return NULL; + ctx->count = count; + ctx->mmio_base = res->mmio_base; + ctx->link_mask = res->link_mask; + ctx->handle = res->handle; + link = ctx->links; + link_mask = ctx->link_mask; /* Create SDW Master devices */ - for (i = 0; i < count; i++) { + for (i = 0; i < count; i++, link++) { if (!(link_mask & BIT(i))) { dev_dbg(&adev->dev, "Link %d masked, will not be enabled\n", i); - link++; continue; } + link->mmio_base = res->mmio_base; link->registers = res->mmio_base + SDW_LINK_BASE - + (SDW_LINK_SIZE * i); + + (SDW_LINK_SIZE * i); link->shim = res->mmio_base + SDW_SHIM_BASE; link->alh = res->mmio_base + SDW_ALH_BASE; @@ -125,7 +204,7 @@ static struct sdw_intel_ctx memset(&pdevinfo, 0, sizeof(pdevinfo)); pdevinfo.parent = res->parent; - pdevinfo.name = "int-sdw"; + pdevinfo.name = "intel-sdw"; pdevinfo.id = i; pdevinfo.fwnode = acpi_fwnode_handle(adev); pdevinfo.data = link; @@ -136,25 +215,63 @@ static struct sdw_intel_ctx dev_err(&adev->dev, "platform device creation failed: %ld\n", PTR_ERR(pdev)); - goto pdev_err; + goto err; } - link->pdev = pdev; - link++; } return ctx; -pdev_err: +err: ctx->count = i; - sdw_intel_cleanup_pdev(ctx); + sdw_intel_cleanup(ctx); return NULL; } +static int +sdw_intel_startup_controller(struct sdw_intel_ctx *ctx) +{ + struct acpi_device *adev; + struct sdw_intel_link_res *link; + u32 caps; + u32 link_mask; + int i; + + if (acpi_bus_get_device(ctx->handle, &adev)) + return -EINVAL; + + /* Check SNDWLCAP.LCOUNT */ + caps = ioread32(ctx->mmio_base + SDW_SHIM_BASE + SDW_SHIM_LCAP); + caps &= GENMASK(2, 0); + + /* Check HW supported vs property value */ + if (caps < ctx->count) { + dev_err(&adev->dev, + "BIOS master count is larger than hardware capabilities\n"); + return -EINVAL; + } + + if (!ctx->links) + return -EINVAL; + + link = ctx->links; + link_mask = ctx->link_mask; + + /* Startup SDW Master devices */ + for (i = 0; i < ctx->count; i++, link++) { + if (!(link_mask & BIT(i))) + continue; + + intel_master_startup(link->pdev); + } + + return 0; +} + static acpi_status sdw_intel_acpi_cb(acpi_handle handle, u32 level, void *cdata, void **return_value) { - struct sdw_intel_res *res = cdata; + struct sdw_intel_acpi_info *info = cdata; struct acpi_device *adev; acpi_status status; u64 adr; @@ -168,7 +285,7 @@ static acpi_status sdw_intel_acpi_cb(acpi_handle handle, u32 level, return AE_NOT_FOUND; } - res->handle = handle; + info->handle = handle; /* * On some Intel platforms, multiple children of the HDAS @@ -185,36 +302,69 @@ static acpi_status sdw_intel_acpi_cb(acpi_handle handle, u32 level, } /** - * sdw_intel_init() - SoundWire Intel init routine + * sdw_intel_acpi_scan() - SoundWire Intel init routine * @parent_handle: ACPI parent handle - * @res: resource data + * @info: description of what firmware/DSDT tables expose * - * This scans the namespace and creates SoundWire link controller devices - * based on the info queried. + * This scans the namespace and queries firmware to figure out which + * links to enable. A follow-up use of sdw_intel_probe() and + * sdw_intel_startup() is required for creation of devices and bus + * startup */ -void *sdw_intel_init(acpi_handle *parent_handle, struct sdw_intel_res *res) +int sdw_intel_acpi_scan(acpi_handle *parent_handle, + struct sdw_intel_acpi_info *info) { acpi_status status; status = acpi_walk_namespace(ACPI_TYPE_DEVICE, parent_handle, 1, sdw_intel_acpi_cb, - NULL, res, NULL); + NULL, info, NULL); if (ACPI_FAILURE(status)) - return NULL; + return -ENODEV; - return sdw_intel_add_controller(res); + return sdw_intel_scan_controller(info); } +EXPORT_SYMBOL(sdw_intel_acpi_scan); +/** + * sdw_intel_probe() - SoundWire Intel probe routine + * @res: resource data + * + * This registers a platform device for each Master handled by the controller, + * and SoundWire Master and Slave devices will be created by the platform + * device probe. All the information necessary is stored in the context, and + * the res argument pointer can be freed after this step. + * This function will be called after sdw_intel_acpi_scan() by SOF probe. + */ +struct sdw_intel_ctx +*sdw_intel_probe(struct sdw_intel_res *res) +{ + return sdw_intel_probe_controller(res); +} +EXPORT_SYMBOL(sdw_intel_probe); + +/** + * sdw_intel_startup() - SoundWire Intel startup + * @ctx: SoundWire context allocated in the probe + * + * Startup Intel SoundWire controller. This function will be called after + * Intel Audio DSP is powered up. + */ +int sdw_intel_startup(struct sdw_intel_ctx *ctx) +{ + return sdw_intel_startup_controller(ctx); +} +EXPORT_SYMBOL(sdw_intel_startup); /** * sdw_intel_exit() - SoundWire Intel exit - * @arg: callback context + * @ctx: SoundWire context allocated in the probe * * Delete the controller instances created and cleanup */ void sdw_intel_exit(struct sdw_intel_ctx *ctx) { - sdw_intel_cleanup_pdev(ctx); + sdw_intel_cleanup(ctx); } EXPORT_SYMBOL(sdw_intel_exit); -- cgit v1.2.3-59-g8ed1b From 51fe3881a29b034029f56da107ce0bb06025e041 Mon Sep 17 00:00:00 2001 From: Rikard Falkeborn Date: Wed, 10 Jun 2020 01:00:29 +0200 Subject: soundwire: qcom: Constify static structs qcom_swrm_port_ops and qcom_swrm_ops are not modified and can be made const to allow the compiler to put them in read-only memory. Before: text data bss dec hex filename 18266 3056 256 21578 544a drivers/soundwire/qcom.o After: text data bss dec hex filename 18426 2896 256 21578 544a drivers/soundwire/qcom.o Signed-off-by: Rikard Falkeborn Link: https://lore.kernel.org/r/20200609230029.69802-1-rikard.falkeborn@gmail.com Signed-off-by: Vinod Koul --- drivers/soundwire/qcom.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/soundwire/qcom.c b/drivers/soundwire/qcom.c index a1c2a44a3b4d..915c2cf0c274 100644 --- a/drivers/soundwire/qcom.c +++ b/drivers/soundwire/qcom.c @@ -406,13 +406,13 @@ static int qcom_swrm_port_enable(struct sdw_bus *bus, return ctrl->reg_write(ctrl, reg, val); } -static struct sdw_master_port_ops qcom_swrm_port_ops = { +static const struct sdw_master_port_ops qcom_swrm_port_ops = { .dpn_set_port_params = qcom_swrm_port_params, .dpn_set_port_transport_params = qcom_swrm_transport_params, .dpn_port_enable_ch = qcom_swrm_port_enable, }; -static struct sdw_master_ops qcom_swrm_ops = { +static const struct sdw_master_ops qcom_swrm_ops = { .xfer_msg = qcom_swrm_xfer_msg, .pre_bank_switch = qcom_swrm_pre_bank_switch, }; -- cgit v1.2.3-59-g8ed1b From 133552bf03edbe3892767a4b64c56e3bed746374 Mon Sep 17 00:00:00 2001 From: Tiezhu Yang Date: Mon, 25 May 2020 21:08:58 +0800 Subject: phy: Remove CONFIG_ARCH_* check for related subdir in Makefile If CONFIG_ARCH_ROCKCHIP is not set but COMPILE_TEST is set, the file in the subdir rockchip can not be built due to CONFIG_ARCH_ROCKCHIP check in drivers/phy/Makefile. Since the related configs in drivers/phy/rockchip/Kconfig depend on ARCH_ROCKCHIP, so remove CONFIG_ARCH_ROCKCHIP check for subdir rockchip in drivers/phy/Makefile. The other CONFIG_ARCH_* about allwinner, amlogic, mediatek, renesas and tegra have the same situation, so remove them too. Signed-off-by: Tiezhu Yang Reviewed-by: Heiko Stuebner Link: https://lore.kernel.org/r/1590412138-13903-2-git-send-email-yangtiezhu@loongson.cn Signed-off-by: Vinod Koul --- drivers/phy/Makefile | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index 310c149a9df5..16e2622277d7 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -8,24 +8,24 @@ obj-$(CONFIG_GENERIC_PHY_MIPI_DPHY) += phy-core-mipi-dphy.o obj-$(CONFIG_PHY_LPC18XX_USB_OTG) += phy-lpc18xx-usb-otg.o obj-$(CONFIG_PHY_XGENE) += phy-xgene.o obj-$(CONFIG_PHY_PISTACHIO_USB) += phy-pistachio-usb.o -obj-$(CONFIG_ARCH_SUNXI) += allwinner/ -obj-$(CONFIG_ARCH_MESON) += amlogic/ -obj-$(CONFIG_ARCH_MEDIATEK) += mediatek/ -obj-$(CONFIG_ARCH_RENESAS) += renesas/ -obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/ -obj-$(CONFIG_ARCH_TEGRA) += tegra/ -obj-y += broadcom/ \ +obj-y += allwinner/ \ + amlogic/ \ + broadcom/ \ cadence/ \ freescale/ \ hisilicon/ \ intel/ \ lantiq/ \ marvell/ \ + mediatek/ \ motorola/ \ mscc/ \ qualcomm/ \ ralink/ \ + renesas/ \ + rockchip/ \ samsung/ \ socionext/ \ st/ \ + tegra/ \ ti/ -- cgit v1.2.3-59-g8ed1b From c233a2edf80de537d195f80fd46d92a7348d9a33 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 17 Jun 2020 17:28:03 +0200 Subject: phy: exynos: Rename Exynos to lowercase Fix up inconsistent usage of upper and lowercase letters in "Exynos" name. "EXYNOS" is not an abbreviation but a regular trademarked name. Therefore it should be written with lowercase letters starting with capital letter. The lowercase "Exynos" name is promoted by its manufacturer Samsung Electronics Co., Ltd., in advertisement materials and on website. Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20200617152803.17941-1-krzk@kernel.org Signed-off-by: Vinod Koul --- drivers/phy/allwinner/phy-sun4i-usb.c | 2 +- drivers/phy/samsung/Kconfig | 8 ++++---- drivers/phy/samsung/phy-exynos-dp-video.c | 4 ++-- drivers/phy/samsung/phy-exynos-mipi-video.c | 4 ++-- drivers/phy/samsung/phy-exynos-pcie.c | 2 +- drivers/phy/samsung/phy-exynos5-usbdrd.c | 6 +++--- drivers/phy/samsung/phy-samsung-usb2.c | 2 +- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/phy/allwinner/phy-sun4i-usb.c b/drivers/phy/allwinner/phy-sun4i-usb.c index 856927382248..7e09ad6a0b42 100644 --- a/drivers/phy/allwinner/phy-sun4i-usb.c +++ b/drivers/phy/allwinner/phy-sun4i-usb.c @@ -7,7 +7,7 @@ * Based on code from * Allwinner Technology Co., Ltd. * - * Modelled after: Samsung S5P/EXYNOS SoC series MIPI CSIS/DSIM DPHY driver + * Modelled after: Samsung S5P/Exynos SoC series MIPI CSIS/DSIM DPHY driver * Copyright (C) 2013 Samsung Electronics Co., Ltd. * Author: Sylwester Nawrocki */ diff --git a/drivers/phy/samsung/Kconfig b/drivers/phy/samsung/Kconfig index 9e483d1fdaf2..19f2e3119343 100644 --- a/drivers/phy/samsung/Kconfig +++ b/drivers/phy/samsung/Kconfig @@ -3,23 +3,23 @@ # Phy drivers for Samsung platforms # config PHY_EXYNOS_DP_VIDEO - tristate "EXYNOS SoC series Display Port PHY driver" + tristate "Exynos SoC series Display Port PHY driver" depends on OF depends on ARCH_EXYNOS || COMPILE_TEST default ARCH_EXYNOS select GENERIC_PHY help - Support for Display Port PHY found on Samsung EXYNOS SoCs. + Support for Display Port PHY found on Samsung Exynos SoCs. config PHY_EXYNOS_MIPI_VIDEO - tristate "S5P/EXYNOS SoC series MIPI CSI-2/DSI PHY driver" + tristate "S5P/Exynos SoC series MIPI CSI-2/DSI PHY driver" depends on HAS_IOMEM depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST select GENERIC_PHY default y if ARCH_S5PV210 || ARCH_EXYNOS help Support for MIPI CSI-2 and MIPI DSI DPHY found on Samsung S5P - and EXYNOS SoCs. + and Exynos SoCs. config PHY_EXYNOS_PCIE bool "Exynos PCIe PHY driver" diff --git a/drivers/phy/samsung/phy-exynos-dp-video.c b/drivers/phy/samsung/phy-exynos-dp-video.c index 6c607df1dc9a..2b670ef91deb 100644 --- a/drivers/phy/samsung/phy-exynos-dp-video.c +++ b/drivers/phy/samsung/phy-exynos-dp-video.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Samsung EXYNOS SoC series Display Port PHY driver + * Samsung Exynos SoC series Display Port PHY driver * * Copyright (C) 2013 Samsung Electronics Co., Ltd. * Author: Jingoo Han @@ -115,5 +115,5 @@ static struct platform_driver exynos_dp_video_phy_driver = { module_platform_driver(exynos_dp_video_phy_driver); MODULE_AUTHOR("Jingoo Han "); -MODULE_DESCRIPTION("Samsung EXYNOS SoC DP PHY driver"); +MODULE_DESCRIPTION("Samsung Exynos SoC DP PHY driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/samsung/phy-exynos-mipi-video.c b/drivers/phy/samsung/phy-exynos-mipi-video.c index bb51195f189f..c1df1ef3ee3c 100644 --- a/drivers/phy/samsung/phy-exynos-mipi-video.c +++ b/drivers/phy/samsung/phy-exynos-mipi-video.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Samsung S5P/EXYNOS SoC series MIPI CSIS/DSIM DPHY driver + * Samsung S5P/Exynos SoC series MIPI CSIS/DSIM DPHY driver * * Copyright (C) 2013,2016 Samsung Electronics Co., Ltd. * Author: Sylwester Nawrocki @@ -364,6 +364,6 @@ static struct platform_driver exynos_mipi_video_phy_driver = { }; module_platform_driver(exynos_mipi_video_phy_driver); -MODULE_DESCRIPTION("Samsung S5P/EXYNOS SoC MIPI CSI-2/DSI PHY driver"); +MODULE_DESCRIPTION("Samsung S5P/Exynos SoC MIPI CSI-2/DSI PHY driver"); MODULE_AUTHOR("Sylwester Nawrocki "); MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/samsung/phy-exynos-pcie.c b/drivers/phy/samsung/phy-exynos-pcie.c index 659e7ae0a6cf..7e28b1aea0d1 100644 --- a/drivers/phy/samsung/phy-exynos-pcie.c +++ b/drivers/phy/samsung/phy-exynos-pcie.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Samsung EXYNOS SoC series PCIe PHY driver + * Samsung Exynos SoC series PCIe PHY driver * * Phy provider for PCIe controller on Exynos SoC series * diff --git a/drivers/phy/samsung/phy-exynos5-usbdrd.c b/drivers/phy/samsung/phy-exynos5-usbdrd.c index e510732afb8b..eb06ce9f748f 100644 --- a/drivers/phy/samsung/phy-exynos5-usbdrd.c +++ b/drivers/phy/samsung/phy-exynos5-usbdrd.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Samsung EXYNOS5 SoC series USB DRD PHY driver + * Samsung Exynos5 SoC series USB DRD PHY driver * * Phy provider for USB 3.0 DRD controller on Exynos5 SoC series * @@ -33,7 +33,7 @@ #define EXYNOS5_FSEL_24MHZ 0x5 #define EXYNOS5_FSEL_50MHZ 0x7 -/* EXYNOS5: USB 3.0 DRD PHY registers */ +/* Exynos5: USB 3.0 DRD PHY registers */ #define EXYNOS5_DRD_LINKSYSTEM 0x04 #define LINKSYSTEM_FLADJ_MASK (0x3f << 1) @@ -958,7 +958,7 @@ static struct platform_driver exynos5_usb3drd_phy = { }; module_platform_driver(exynos5_usb3drd_phy); -MODULE_DESCRIPTION("Samsung EXYNOS5 SoCs USB 3.0 DRD controller PHY driver"); +MODULE_DESCRIPTION("Samsung Exynos5 SoCs USB 3.0 DRD controller PHY driver"); MODULE_AUTHOR("Vivek Gautam "); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:exynos5_usb3drd_phy"); diff --git a/drivers/phy/samsung/phy-samsung-usb2.c b/drivers/phy/samsung/phy-samsung-usb2.c index 090aa02e02de..a3ed3ff04690 100644 --- a/drivers/phy/samsung/phy-samsung-usb2.c +++ b/drivers/phy/samsung/phy-samsung-usb2.c @@ -255,7 +255,7 @@ static struct platform_driver samsung_usb2_phy_driver = { }; module_platform_driver(samsung_usb2_phy_driver); -MODULE_DESCRIPTION("Samsung S5P/EXYNOS SoC USB PHY driver"); +MODULE_DESCRIPTION("Samsung S5P/Exynos SoC USB PHY driver"); MODULE_AUTHOR("Kamil Debski "); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:samsung-usb2-phy"); -- cgit v1.2.3-59-g8ed1b From 066571516bea4dd903ae077a00df802a19b71248 Mon Sep 17 00:00:00 2001 From: Sivaprakash Murugesan Date: Mon, 8 Jun 2020 19:41:15 +0530 Subject: dt-bindings: phy: qcom,qmp: Add ipq8074 usb dt bindings Add ipq8074 qmp phy device compatible for super speed usb support. Signed-off-by: Sivaprakash Murugesan Tested-by: Sricharan R Acked-by: Rob Herring Link: https://lore.kernel.org/r/1591625479-4483-2-git-send-email-sivaprak@codeaurora.org Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml index f80f8896d527..e4cd4a1deae9 100644 --- a/Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml @@ -18,6 +18,7 @@ properties: compatible: enum: - qcom,ipq8074-qmp-pcie-phy + - qcom,ipq8074-qmp-usb3-phy - qcom,msm8996-qmp-pcie-phy - qcom,msm8996-qmp-ufs-phy - qcom,msm8996-qmp-usb3-phy @@ -161,6 +162,7 @@ allOf: compatible: contains: enum: + - qcom,ipq8074-qmp-usb3-phy - qcom,msm8996-qmp-usb3-phy - qcom,msm8998-qmp-pcie-phy - qcom,msm8998-qmp-usb3-phy -- cgit v1.2.3-59-g8ed1b From ba971c2ff59954c47de8c864adf9fc8f27863208 Mon Sep 17 00:00:00 2001 From: Sivaprakash Murugesan Date: Mon, 8 Jun 2020 19:41:16 +0530 Subject: dt-bindings: phy: qcom,qusb2: Add ipq8074 device compatible Add ipq8074 compatible in QUSB2 PHY for high speed USB support. Signed-off-by: Sivaprakash Murugesan Tested-by: Sricharan R Acked-by: Rob Herring Link: https://lore.kernel.org/r/1591625479-4483-3-git-send-email-sivaprak@codeaurora.org Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/phy/qcom,qusb2-phy.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/phy/qcom,qusb2-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,qusb2-phy.yaml index b5a6195de7ff..9ba62dcb1e5d 100644 --- a/Documentation/devicetree/bindings/phy/qcom,qusb2-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,qusb2-phy.yaml @@ -18,6 +18,7 @@ properties: oneOf: - items: - enum: + - qcom,ipq8074-qusb2-phy - qcom,msm8996-qusb2-phy - qcom,msm8998-qusb2-phy - items: -- cgit v1.2.3-59-g8ed1b From 507156f5a99fa03c0dce8281ff3d26fbf473630c Mon Sep 17 00:00:00 2001 From: Sivaprakash Murugesan Date: Mon, 8 Jun 2020 19:41:17 +0530 Subject: phy: qcom-qmp: Add USB QMP PHY support for IPQ8074 Add QMP USB PHY found in IPQ8074 Co-developed-by: Balaji Prakash J Signed-off-by: Balaji Prakash J Signed-off-by: Sivaprakash Murugesan Tested-by: Sricharan R Reviewed-by: Bjorn Andersson Link: https://lore.kernel.org/r/1591625479-4483-4-git-send-email-sivaprak@codeaurora.org Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-qmp.c | 102 ++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.c b/drivers/phy/qualcomm/phy-qcom-qmp.c index e91040af3394..544f140553d4 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp.c @@ -198,6 +198,81 @@ static const unsigned int sm8150_ufsphy_regs_layout[QPHY_LAYOUT_SIZE] = { [QPHY_SW_RESET] = QPHY_V4_PCS_UFS_SW_RESET, }; +static const struct qmp_phy_init_tbl ipq8074_usb3_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x08), + QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x30), + QMP_PHY_INIT_CFG(QSERDES_COM_BG_TRIM, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_COM_SVS_MODE_CLK_SEL, 0x01), + QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x06), + QMP_PHY_INIT_CFG(QSERDES_COM_PLL_IVCO, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_COM_SYS_CLK_CTRL, 0x06), + /* PLL and Loop filter settings */ + QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE0, 0x82), + QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x55), + QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x55), + QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x03), + QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE0, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE0, 0x28), + QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE0, 0x15), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE0, 0x34), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_CORE_CLK_EN, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_CFG, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_BG_TIMER, 0x0a), + /* SSC settings */ + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_EN_CENTER, 0x01), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER1, 0x31), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER2, 0x01), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER2, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE1, 0xde), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE2, 0x07), +}; + +static const struct qmp_phy_init_tbl ipq8074_usb3_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_GAIN, 0x06), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x02), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4c), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4, 0xb8), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80), + QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_CNTRL, 0x03), + QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x16), + QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_ENABLES, 0x0), +}; + +static const struct qmp_phy_init_tbl ipq8074_usb3_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V0, 0x15), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V0, 0x0e), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL2, 0x83), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL1, 0x02), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_L, 0x09), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_H_TOL, 0xa2), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_MAN_CODE, 0x85), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG1, 0xd1), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG2, 0x1f), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG3, 0x47), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_POWER_STATE_CONFIG2, 0x1b), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_WAIT_TIME, 0x75), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_RUN_TIME, 0x13), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_LFPS_TX_ECSTART_EQTLOCK, 0x86), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x04), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TSYNC_RSYNC_TIME, 0x44), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_L, 0xe7), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_H, 0x03), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_L, 0x40), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_H, 0x00), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_SIGDET_LVL, 0x88), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V0, 0x17), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V0, 0x0f), +}; + static const struct qmp_phy_init_tbl msm8996_pcie_serdes_tbl[] = { QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x1c), QMP_PHY_INIT_CFG(QSERDES_COM_CLK_ENABLE1, 0x10), @@ -1593,6 +1668,30 @@ static const char * const qmp_phy_vreg_l[] = { "vdda-phy", "vdda-pll", }; +static const struct qmp_phy_cfg ipq8074_usb3phy_cfg = { + .type = PHY_TYPE_USB3, + .nlanes = 1, + + .serdes_tbl = ipq8074_usb3_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(ipq8074_usb3_serdes_tbl), + .tx_tbl = msm8996_usb3_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(msm8996_usb3_tx_tbl), + .rx_tbl = ipq8074_usb3_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(ipq8074_usb3_rx_tbl), + .pcs_tbl = ipq8074_usb3_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(ipq8074_usb3_pcs_tbl), + .clk_list = msm8996_phy_clk_l, + .num_clks = ARRAY_SIZE(msm8996_phy_clk_l), + .reset_list = msm8996_usb3phy_reset_l, + .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = usb3phy_regs_layout, + + .start_ctrl = SERDES_START | PCS_START, + .pwrdn_ctrl = SW_PWRDN, +}; + static const struct qmp_phy_cfg msm8996_pciephy_cfg = { .type = PHY_TYPE_PCIE, .nlanes = 3, @@ -2699,6 +2798,9 @@ int qcom_qmp_phy_create(struct device *dev, struct device_node *np, int id) static const struct of_device_id qcom_qmp_phy_of_match_table[] = { { + .compatible = "qcom,ipq8074-qmp-usb3-phy", + .data = &ipq8074_usb3phy_cfg, + }, { .compatible = "qcom,msm8996-qmp-pcie-phy", .data = &msm8996_pciephy_cfg, }, { -- cgit v1.2.3-59-g8ed1b From 1d99d491dc96f4690534c67fd7361e2e74dd4c70 Mon Sep 17 00:00:00 2001 From: Sivaprakash Murugesan Date: Mon, 8 Jun 2020 19:41:18 +0530 Subject: phy: qcom-qusb2: Add ipq8074 device compatible Add ipq8074 qusb2 device compatible for high speed usb support. Signed-off-by: Sivaprakash Murugesan Tested-by: Sricharan R Reviewed-by: Sricharan R Link: https://lore.kernel.org/r/1591625479-4483-5-git-send-email-sivaprak@codeaurora.org Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-qusb2.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/phy/qualcomm/phy-qcom-qusb2.c b/drivers/phy/qualcomm/phy-qcom-qusb2.c index 393011a05b48..557547dabfd5 100644 --- a/drivers/phy/qualcomm/phy-qcom-qusb2.c +++ b/drivers/phy/qualcomm/phy-qcom-qusb2.c @@ -810,6 +810,9 @@ static const struct phy_ops qusb2_phy_gen_ops = { static const struct of_device_id qusb2_phy_of_match_table[] = { { + .compatible = "qcom,ipq8074-qusb2-phy", + .data = &msm8996_phy_cfg, + }, { .compatible = "qcom,msm8996-qusb2-phy", .data = &msm8996_phy_cfg, }, { -- cgit v1.2.3-59-g8ed1b From a5d0fbab4e0b42fb61b542b8c8c4c2ba50dc32d8 Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Mon, 22 Jun 2020 15:37:22 +0200 Subject: dt-bindings: fpga: xilinx-slave-serial: add optional INIT_B GPIO The INIT_B is used by the 6 and 7 series to report the programming status, providing more control and information about programming errors. Signed-off-by: Luca Ceresoli Signed-off-by: Moritz Fischer --- Documentation/devicetree/bindings/fpga/xilinx-slave-serial.txt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/fpga/xilinx-slave-serial.txt b/Documentation/devicetree/bindings/fpga/xilinx-slave-serial.txt index 9f103f3872e8..5ef659c1394d 100644 --- a/Documentation/devicetree/bindings/fpga/xilinx-slave-serial.txt +++ b/Documentation/devicetree/bindings/fpga/xilinx-slave-serial.txt @@ -16,6 +16,10 @@ Required properties: - prog_b-gpios: config pin (referred to as PROGRAM_B in the manual) - done-gpios: config status pin (referred to as DONE in the manual) +Optional properties: +- init-b-gpios: initialization status and configuration error pin + (referred to as INIT_B in the manual) + Example for full FPGA configuration: fpga-region0 { @@ -40,7 +44,8 @@ Example for full FPGA configuration: spi-max-frequency = <60000000>; spi-cpha; reg = <0>; - done-gpios = <&gpio0 9 GPIO_ACTIVE_HIGH>; prog_b-gpios = <&gpio0 29 GPIO_ACTIVE_LOW>; + init-b-gpios = <&gpio0 28 GPIO_ACTIVE_LOW>; + done-gpios = <&gpio0 9 GPIO_ACTIVE_HIGH>; }; }; -- cgit v1.2.3-59-g8ed1b From dd2784c01d93db20252a6416f3007cbbb89e4758 Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Mon, 22 Jun 2020 15:37:23 +0200 Subject: fpga manager: xilinx-spi: check INIT_B pin during write_init The INIT_B pin reports the status during startup and after the end of the programming process. However the current driver completely ignores it. Check the pin status during startup to make sure programming is never started too early and also to detect any hardware issues in the FPGA connection. This is optional for backward compatibility. If INIT_B is not passed by device tree, just fallback to the old udelays. Signed-off-by: Luca Ceresoli Signed-off-by: Moritz Fischer --- drivers/fpga/xilinx-spi.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/drivers/fpga/xilinx-spi.c b/drivers/fpga/xilinx-spi.c index 799ae04301be..2967aa2a74e2 100644 --- a/drivers/fpga/xilinx-spi.c +++ b/drivers/fpga/xilinx-spi.c @@ -23,6 +23,7 @@ struct xilinx_spi_conf { struct spi_device *spi; struct gpio_desc *prog_b; + struct gpio_desc *init_b; struct gpio_desc *done; }; @@ -36,11 +37,45 @@ static enum fpga_mgr_states xilinx_spi_state(struct fpga_manager *mgr) return FPGA_MGR_STATE_UNKNOWN; } +/** + * wait_for_init_b - wait for the INIT_B pin to have a given state, or wait + * a given delay if the pin is unavailable + * + * @mgr: The FPGA manager object + * @value: Value INIT_B to wait for (1 = asserted = low) + * @alt_udelay: Delay to wait if the INIT_B GPIO is not available + * + * Returns 0 when the INIT_B GPIO reached the given state or -ETIMEDOUT if + * too much time passed waiting for that. If no INIT_B GPIO is available + * then always return 0. + */ +static int wait_for_init_b(struct fpga_manager *mgr, int value, + unsigned long alt_udelay) +{ + struct xilinx_spi_conf *conf = mgr->priv; + unsigned long timeout = jiffies + msecs_to_jiffies(1000); + + if (conf->init_b) { + while (time_before(jiffies, timeout)) { + /* dump_state(conf, "wait for init_d .."); */ + if (gpiod_get_value(conf->init_b) == value) + return 0; + usleep_range(100, 400); + } + return -ETIMEDOUT; + } + + udelay(alt_udelay); + + return 0; +} + static int xilinx_spi_write_init(struct fpga_manager *mgr, struct fpga_image_info *info, const char *buf, size_t count) { struct xilinx_spi_conf *conf = mgr->priv; + int err; if (info->flags & FPGA_MGR_PARTIAL_RECONFIG) { dev_err(&mgr->dev, "Partial reconfiguration not supported.\n"); @@ -49,10 +84,21 @@ static int xilinx_spi_write_init(struct fpga_manager *mgr, gpiod_set_value(conf->prog_b, 1); - udelay(1); /* min is 500 ns */ + err = wait_for_init_b(mgr, 1, 1); /* min is 500 ns */ + if (err) { + dev_err(&mgr->dev, "INIT_B pin did not go low\n"); + gpiod_set_value(conf->prog_b, 0); + return err; + } gpiod_set_value(conf->prog_b, 0); + err = wait_for_init_b(mgr, 0, 0); + if (err) { + dev_err(&mgr->dev, "INIT_B pin did not go high\n"); + return err; + } + if (gpiod_get_value(conf->done)) { dev_err(&mgr->dev, "Unexpected DONE pin state...\n"); return -EIO; @@ -154,6 +200,13 @@ static int xilinx_spi_probe(struct spi_device *spi) return PTR_ERR(conf->prog_b); } + conf->init_b = devm_gpiod_get_optional(&spi->dev, "init-b", GPIOD_IN); + if (IS_ERR(conf->init_b)) { + dev_err(&spi->dev, "Failed to get INIT_B gpio: %ld\n", + PTR_ERR(conf->init_b)); + return PTR_ERR(conf->init_b); + } + conf->done = devm_gpiod_get(&spi->dev, "done", GPIOD_IN); if (IS_ERR(conf->done)) { dev_err(&spi->dev, "Failed to get DONE gpio: %ld\n", -- cgit v1.2.3-59-g8ed1b From 8d021039cbb5e358c7c07c562811253a61c8d551 Mon Sep 17 00:00:00 2001 From: Xu Yilun Date: Tue, 16 Jun 2020 12:08:42 +0800 Subject: fpga: dfl: parse interrupt info for feature devices on enumeration DFL based FPGA devices could support interrupts for different purposes, but current DFL framework only supports feature device enumeration with given MMIO resources information via common DFL headers. This patch introduces one new API dfl_fpga_enum_info_add_irq for low level bus drivers (e.g. PCIe device driver) to pass its interrupt resources information to DFL framework for enumeration, and also adds interrupt enumeration code in framework to parse and assign interrupt resources for enumerated feature devices and their own sub features. With this patch, DFL framework enumerates interrupt resources for core features, including PORT Error Reporting, FME (FPGA Management Engine) Error Reporting and also AFU User Interrupts. Signed-off-by: Luwei Kang Signed-off-by: Wu Hao Signed-off-by: Xu Yilun Reviewed-by: Marcelo Tosatti Acked-by: Wu Hao Signed-off-by: Moritz Fischer --- drivers/fpga/dfl.c | 153 +++++++++++++++++++++++++++++++++++++++++++++++++++++ drivers/fpga/dfl.h | 41 ++++++++++++++ 2 files changed, 194 insertions(+) diff --git a/drivers/fpga/dfl.c b/drivers/fpga/dfl.c index 2dd13e036d45..d915331fb52c 100644 --- a/drivers/fpga/dfl.c +++ b/drivers/fpga/dfl.c @@ -421,6 +421,9 @@ EXPORT_SYMBOL_GPL(dfl_fpga_dev_ops_unregister); * * @dev: device to enumerate. * @cdev: the container device for all feature devices. + * @nr_irqs: number of irqs for all feature devices. + * @irq_table: Linux IRQ numbers for all irqs, indexed by local irq index of + * this device. * @feature_dev: current feature device. * @ioaddr: header register region address of feature device in enumeration. * @sub_features: a sub features linked list for feature device in enumeration. @@ -429,6 +432,9 @@ EXPORT_SYMBOL_GPL(dfl_fpga_dev_ops_unregister); struct build_feature_devs_info { struct device *dev; struct dfl_fpga_cdev *cdev; + unsigned int nr_irqs; + int *irq_table; + struct platform_device *feature_dev; void __iomem *ioaddr; struct list_head sub_features; @@ -442,12 +448,16 @@ struct build_feature_devs_info { * @mmio_res: mmio resource of this sub feature. * @ioaddr: mapped base address of mmio resource. * @node: node in sub_features linked list. + * @irq_base: start of irq index in this sub feature. + * @nr_irqs: number of irqs of this sub feature. */ struct dfl_feature_info { u64 fid; struct resource mmio_res; void __iomem *ioaddr; struct list_head node; + unsigned int irq_base; + unsigned int nr_irqs; }; static void dfl_fpga_cdev_add_port_dev(struct dfl_fpga_cdev *cdev, @@ -519,6 +529,8 @@ static int build_info_commit_dev(struct build_feature_devs_info *binfo) /* fill features and resource information for feature dev */ list_for_each_entry_safe(finfo, p, &binfo->sub_features, node) { struct dfl_feature *feature = &pdata->features[index]; + struct dfl_feature_irq_ctx *ctx; + unsigned int i; /* save resource information for each feature */ feature->id = finfo->fid; @@ -526,6 +538,20 @@ static int build_info_commit_dev(struct build_feature_devs_info *binfo) feature->ioaddr = finfo->ioaddr; fdev->resource[index++] = finfo->mmio_res; + if (finfo->nr_irqs) { + ctx = devm_kcalloc(binfo->dev, finfo->nr_irqs, + sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + for (i = 0; i < finfo->nr_irqs; i++) + ctx[i].irq = + binfo->irq_table[finfo->irq_base + i]; + + feature->irq_ctx = ctx; + feature->nr_irqs = finfo->nr_irqs; + } + list_del(&finfo->node); kfree(finfo); } @@ -637,6 +663,78 @@ static u64 feature_id(void __iomem *start) return 0; } +static int parse_feature_irqs(struct build_feature_devs_info *binfo, + resource_size_t ofst, u64 fid, + unsigned int *irq_base, unsigned int *nr_irqs) +{ + void __iomem *base = binfo->ioaddr + ofst; + unsigned int i, ibase, inr = 0; + int virq; + u64 v; + + /* + * Ideally DFL framework should only read info from DFL header, but + * current version DFL only provides mmio resources information for + * each feature in DFL Header, no field for interrupt resources. + * Interrupt resource information is provided by specific mmio + * registers of each private feature which supports interrupt. So in + * order to parse and assign irq resources, DFL framework has to look + * into specific capability registers of these private features. + * + * Once future DFL version supports generic interrupt resource + * information in common DFL headers, the generic interrupt parsing + * code will be added. But in order to be compatible to old version + * DFL, the driver may still fall back to these quirks. + */ + switch (fid) { + case PORT_FEATURE_ID_UINT: + v = readq(base + PORT_UINT_CAP); + ibase = FIELD_GET(PORT_UINT_CAP_FST_VECT, v); + inr = FIELD_GET(PORT_UINT_CAP_INT_NUM, v); + break; + case PORT_FEATURE_ID_ERROR: + v = readq(base + PORT_ERROR_CAP); + ibase = FIELD_GET(PORT_ERROR_CAP_INT_VECT, v); + inr = FIELD_GET(PORT_ERROR_CAP_SUPP_INT, v); + break; + case FME_FEATURE_ID_GLOBAL_ERR: + v = readq(base + FME_ERROR_CAP); + ibase = FIELD_GET(FME_ERROR_CAP_INT_VECT, v); + inr = FIELD_GET(FME_ERROR_CAP_SUPP_INT, v); + break; + } + + if (!inr) { + *irq_base = 0; + *nr_irqs = 0; + return 0; + } + + dev_dbg(binfo->dev, "feature: 0x%llx, irq_base: %u, nr_irqs: %u\n", + fid, ibase, inr); + + if (ibase + inr > binfo->nr_irqs) { + dev_err(binfo->dev, + "Invalid interrupt number in feature 0x%llx\n", fid); + return -EINVAL; + } + + for (i = 0; i < inr; i++) { + virq = binfo->irq_table[ibase + i]; + if (virq < 0 || virq > NR_IRQS) { + dev_err(binfo->dev, + "Invalid irq table entry for feature 0x%llx\n", + fid); + return -EINVAL; + } + } + + *irq_base = ibase; + *nr_irqs = inr; + + return 0; +} + /* * when create sub feature instances, for private features, it doesn't need * to provide resource size and feature id as they could be read from DFH @@ -649,7 +747,9 @@ create_feature_instance(struct build_feature_devs_info *binfo, struct dfl_fpga_enum_dfl *dfl, resource_size_t ofst, resource_size_t size, u64 fid) { + unsigned int irq_base, nr_irqs; struct dfl_feature_info *finfo; + int ret; /* read feature size and id if inputs are invalid */ size = size ? size : feature_size(dfl->ioaddr + ofst); @@ -658,6 +758,10 @@ create_feature_instance(struct build_feature_devs_info *binfo, if (dfl->len - ofst < size) return -EINVAL; + ret = parse_feature_irqs(binfo, ofst, fid, &irq_base, &nr_irqs); + if (ret) + return ret; + finfo = kzalloc(sizeof(*finfo), GFP_KERNEL); if (!finfo) return -ENOMEM; @@ -666,6 +770,8 @@ create_feature_instance(struct build_feature_devs_info *binfo, finfo->mmio_res.start = dfl->start + ofst; finfo->mmio_res.end = finfo->mmio_res.start + size - 1; finfo->mmio_res.flags = IORESOURCE_MEM; + finfo->irq_base = irq_base; + finfo->nr_irqs = nr_irqs; finfo->ioaddr = dfl->ioaddr + ofst; list_add_tail(&finfo->node, &binfo->sub_features); @@ -852,6 +958,10 @@ void dfl_fpga_enum_info_free(struct dfl_fpga_enum_info *info) devm_kfree(dev, dfl); } + /* remove irq table */ + if (info->irq_table) + devm_kfree(dev, info->irq_table); + devm_kfree(dev, info); put_device(dev); } @@ -891,6 +1001,45 @@ int dfl_fpga_enum_info_add_dfl(struct dfl_fpga_enum_info *info, } EXPORT_SYMBOL_GPL(dfl_fpga_enum_info_add_dfl); +/** + * dfl_fpga_enum_info_add_irq - add irq table to enum info + * + * @info: ptr to dfl_fpga_enum_info + * @nr_irqs: number of irqs of the DFL fpga device to be enumerated. + * @irq_table: Linux IRQ numbers for all irqs, indexed by local irq index of + * this device. + * + * One FPGA device may have several interrupts. This function adds irq + * information of the DFL fpga device to enum info for next step enumeration. + * This function should be called before dfl_fpga_feature_devs_enumerate(). + * As we only support one irq domain for all DFLs in the same enum info, adding + * irq table a second time for the same enum info will return error. + * + * If we need to enumerate DFLs which belong to different irq domains, we + * should fill more enum info and enumerate them one by one. + * + * Return: 0 on success, negative error code otherwise. + */ +int dfl_fpga_enum_info_add_irq(struct dfl_fpga_enum_info *info, + unsigned int nr_irqs, int *irq_table) +{ + if (!nr_irqs || !irq_table) + return -EINVAL; + + if (info->irq_table) + return -EEXIST; + + info->irq_table = devm_kmemdup(info->dev, irq_table, + sizeof(int) * nr_irqs, GFP_KERNEL); + if (!info->irq_table) + return -ENOMEM; + + info->nr_irqs = nr_irqs; + + return 0; +} +EXPORT_SYMBOL_GPL(dfl_fpga_enum_info_add_irq); + static int remove_feature_dev(struct device *dev, void *data) { struct platform_device *pdev = to_platform_device(dev); @@ -958,6 +1107,10 @@ dfl_fpga_feature_devs_enumerate(struct dfl_fpga_enum_info *info) binfo->dev = info->dev; binfo->cdev = cdev; + binfo->nr_irqs = info->nr_irqs; + if (info->nr_irqs) + binfo->irq_table = info->irq_table; + /* * start enumeration for all feature devices based on Device Feature * Lists. diff --git a/drivers/fpga/dfl.h b/drivers/fpga/dfl.h index 044b0e88e5a8..e1f3ab86560e 100644 --- a/drivers/fpga/dfl.h +++ b/drivers/fpga/dfl.h @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -112,6 +113,13 @@ #define FME_PORT_OFST_ACC_VF 1 #define FME_PORT_OFST_IMP BIT_ULL(60) +/* FME Error Capability Register */ +#define FME_ERROR_CAP 0x70 + +/* FME Error Capability Register Bitfield */ +#define FME_ERROR_CAP_SUPP_INT BIT_ULL(0) /* Interrupt Support */ +#define FME_ERROR_CAP_INT_VECT GENMASK_ULL(12, 1) /* Interrupt vector */ + /* PORT Header Register Set */ #define PORT_HDR_DFH DFH #define PORT_HDR_GUID_L GUID_L @@ -145,6 +153,20 @@ #define PORT_STS_PWR_STATE_AP2 2 /* 90% throttling */ #define PORT_STS_PWR_STATE_AP6 6 /* 100% throttling */ +/* Port Error Capability Register */ +#define PORT_ERROR_CAP 0x38 + +/* Port Error Capability Register Bitfield */ +#define PORT_ERROR_CAP_SUPP_INT BIT_ULL(0) /* Interrupt Support */ +#define PORT_ERROR_CAP_INT_VECT GENMASK_ULL(12, 1) /* Interrupt vector */ + +/* Port Uint Capability Register */ +#define PORT_UINT_CAP 0x8 + +/* Port Uint Capability Register Bitfield */ +#define PORT_UINT_CAP_INT_NUM GENMASK_ULL(11, 0) /* Interrupts num */ +#define PORT_UINT_CAP_FST_VECT GENMASK_ULL(23, 12) /* First Vector */ + /** * struct dfl_fpga_port_ops - port ops * @@ -188,6 +210,15 @@ struct dfl_feature_driver { const struct dfl_feature_ops *ops; }; +/** + * struct dfl_feature_irq_ctx - dfl private feature interrupt context + * + * @irq: Linux IRQ number of this interrupt. + */ +struct dfl_feature_irq_ctx { + int irq; +}; + /** * struct dfl_feature - sub feature of the feature devices * @@ -196,6 +227,8 @@ struct dfl_feature_driver { * this index is used to find its mmio resource from the * feature dev (platform device)'s reources. * @ioaddr: mapped mmio resource address. + * @irq_ctx: interrupt context list. + * @nr_irqs: number of interrupt contexts. * @ops: ops of this sub feature. * @priv: priv data of this feature. */ @@ -203,6 +236,8 @@ struct dfl_feature { u64 id; int resource_index; void __iomem *ioaddr; + struct dfl_feature_irq_ctx *irq_ctx; + unsigned int nr_irqs; const struct dfl_feature_ops *ops; void *priv; }; @@ -384,10 +419,14 @@ static inline u8 dfl_feature_revision(void __iomem *base) * * @dev: parent device. * @dfls: list of device feature lists. + * @nr_irqs: number of irqs for all feature devices. + * @irq_table: Linux IRQ numbers for all irqs, indexed by hw irq numbers. */ struct dfl_fpga_enum_info { struct device *dev; struct list_head dfls; + unsigned int nr_irqs; + int *irq_table; }; /** @@ -411,6 +450,8 @@ struct dfl_fpga_enum_info *dfl_fpga_enum_info_alloc(struct device *dev); int dfl_fpga_enum_info_add_dfl(struct dfl_fpga_enum_info *info, resource_size_t start, resource_size_t len, void __iomem *ioaddr); +int dfl_fpga_enum_info_add_irq(struct dfl_fpga_enum_info *info, + unsigned int nr_irqs, int *irq_table); void dfl_fpga_enum_info_free(struct dfl_fpga_enum_info *info); /** -- cgit v1.2.3-59-g8ed1b From bfef946dbe1bbe6cae97bba27594e8d5b0e01ffa Mon Sep 17 00:00:00 2001 From: Xu Yilun Date: Tue, 16 Jun 2020 12:08:43 +0800 Subject: fpga: dfl: pci: add irq info for feature devices enumeration Some DFL FPGA PCIe cards (e.g. Intel FPGA Programmable Acceleration Card) support MSI-X based interrupts. This patch allows PCIe driver to prepare and pass interrupt resources to DFL via enumeration API. These interrupt resources could then be assigned to actual features which use them. Signed-off-by: Luwei Kang Signed-off-by: Wu Hao Signed-off-by: Xu Yilun Signed-off-by: Tom Rix Reviewed-by: Marcelo Tosatti Acked-by: Wu Hao Signed-off-by: Moritz Fischer --- drivers/fpga/dfl-pci.c | 76 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 67 insertions(+), 9 deletions(-) diff --git a/drivers/fpga/dfl-pci.c b/drivers/fpga/dfl-pci.c index 538755062ab7..4a14a24b505e 100644 --- a/drivers/fpga/dfl-pci.c +++ b/drivers/fpga/dfl-pci.c @@ -39,6 +39,27 @@ static void __iomem *cci_pci_ioremap_bar(struct pci_dev *pcidev, int bar) return pcim_iomap_table(pcidev)[bar]; } +static int cci_pci_alloc_irq(struct pci_dev *pcidev) +{ + int ret, nvec = pci_msix_vec_count(pcidev); + + if (nvec <= 0) { + dev_dbg(&pcidev->dev, "fpga interrupt not supported\n"); + return 0; + } + + ret = pci_alloc_irq_vectors(pcidev, nvec, nvec, PCI_IRQ_MSIX); + if (ret < 0) + return ret; + + return nvec; +} + +static void cci_pci_free_irq(struct pci_dev *pcidev) +{ + pci_free_irq_vectors(pcidev); +} + /* PCI Device ID */ #define PCIE_DEVICE_ID_PF_INT_5_X 0xBCBD #define PCIE_DEVICE_ID_PF_INT_6_X 0xBCC0 @@ -78,17 +99,34 @@ static void cci_remove_feature_devs(struct pci_dev *pcidev) /* remove all children feature devices */ dfl_fpga_feature_devs_remove(drvdata->cdev); + cci_pci_free_irq(pcidev); +} + +static int *cci_pci_create_irq_table(struct pci_dev *pcidev, unsigned int nvec) +{ + unsigned int i; + int *table; + + table = kcalloc(nvec, sizeof(int), GFP_KERNEL); + if (!table) + return table; + + for (i = 0; i < nvec; i++) + table[i] = pci_irq_vector(pcidev, i); + + return table; } /* enumerate feature devices under pci device */ static int cci_enumerate_feature_devs(struct pci_dev *pcidev) { struct cci_drvdata *drvdata = pci_get_drvdata(pcidev); + int port_num, bar, i, nvec, ret = 0; struct dfl_fpga_enum_info *info; struct dfl_fpga_cdev *cdev; resource_size_t start, len; - int port_num, bar, i, ret = 0; void __iomem *base; + int *irq_table; u32 offset; u64 v; @@ -97,11 +135,30 @@ static int cci_enumerate_feature_devs(struct pci_dev *pcidev) if (!info) return -ENOMEM; + /* add irq info for enumeration if the device support irq */ + nvec = cci_pci_alloc_irq(pcidev); + if (nvec < 0) { + dev_err(&pcidev->dev, "Fail to alloc irq %d.\n", nvec); + ret = nvec; + goto enum_info_free_exit; + } else if (nvec) { + irq_table = cci_pci_create_irq_table(pcidev, nvec); + if (!irq_table) { + ret = -ENOMEM; + goto irq_free_exit; + } + + ret = dfl_fpga_enum_info_add_irq(info, nvec, irq_table); + kfree(irq_table); + if (ret) + goto irq_free_exit; + } + /* start to find Device Feature List from Bar 0 */ base = cci_pci_ioremap_bar(pcidev, 0); if (!base) { ret = -ENOMEM; - goto enum_info_free_exit; + goto irq_free_exit; } /* @@ -154,7 +211,7 @@ static int cci_enumerate_feature_devs(struct pci_dev *pcidev) dfl_fpga_enum_info_add_dfl(info, start, len, base); } else { ret = -ENODEV; - goto enum_info_free_exit; + goto irq_free_exit; } /* start enumeration with prepared enumeration information */ @@ -162,11 +219,14 @@ static int cci_enumerate_feature_devs(struct pci_dev *pcidev) if (IS_ERR(cdev)) { dev_err(&pcidev->dev, "Enumeration failure\n"); ret = PTR_ERR(cdev); - goto enum_info_free_exit; + goto irq_free_exit; } drvdata->cdev = cdev; +irq_free_exit: + if (ret) + cci_pci_free_irq(pcidev); enum_info_free_exit: dfl_fpga_enum_info_free(info); @@ -211,12 +271,10 @@ int cci_pci_probe(struct pci_dev *pcidev, const struct pci_device_id *pcidevid) } ret = cci_enumerate_feature_devs(pcidev); - if (ret) { - dev_err(&pcidev->dev, "enumeration failure %d.\n", ret); - goto disable_error_report_exit; - } + if (!ret) + return ret; - return ret; + dev_err(&pcidev->dev, "enumeration failure %d.\n", ret); disable_error_report_exit: pci_disable_pcie_error_reporting(pcidev); -- cgit v1.2.3-59-g8ed1b From 322b598be4d9b9090cda560c4caab78704615ab4 Mon Sep 17 00:00:00 2001 From: Xu Yilun Date: Tue, 16 Jun 2020 12:08:44 +0800 Subject: fpga: dfl: introduce interrupt trigger setting API FPGA user applications may be interested in interrupts generated by DFL features. For example, users can implement their own FPGA logics with interrupts enabled in AFU (Accelerated Function Unit, dynamic region of DFL based FPGA). So user applications need to be notified to handle these interrupts. In order to allow userspace applications to monitor interrupts, driver requires userspace to provide eventfds as interrupt notification channels. Applications then poll/select on the eventfds to get notified. This patch introduces a generic helper functions to do eventfds binding with given interrupts. Sub feature drivers are expected to use XXX_GET_IRQ_NUM to query irq info, and XXX_SET_IRQ to set eventfds for interrupts. This patch also introduces helper functions for these 2 ioctls. Signed-off-by: Luwei Kang Signed-off-by: Wu Hao Signed-off-by: Xu Yilun Signed-off-by: Tom Rix Reviewed-by: Marcelo Tosatti Acked-by: Wu Hao Signed-off-by: Moritz Fischer --- drivers/fpga/dfl.c | 157 ++++++++++++++++++++++++++++++++++++++++++ drivers/fpga/dfl.h | 16 +++++ include/uapi/linux/fpga-dfl.h | 13 ++++ 3 files changed, 186 insertions(+) diff --git a/drivers/fpga/dfl.c b/drivers/fpga/dfl.c index d915331fb52c..649958a36e62 100644 --- a/drivers/fpga/dfl.c +++ b/drivers/fpga/dfl.c @@ -10,7 +10,9 @@ * Wu Hao * Xiao Guangrong */ +#include #include +#include #include "dfl.h" @@ -533,6 +535,7 @@ static int build_info_commit_dev(struct build_feature_devs_info *binfo) unsigned int i; /* save resource information for each feature */ + feature->dev = fdev; feature->id = finfo->fid; feature->resource_index = index; feature->ioaddr = finfo->ioaddr; @@ -1393,6 +1396,160 @@ done: } EXPORT_SYMBOL_GPL(dfl_fpga_cdev_config_ports_vf); +static irqreturn_t dfl_irq_handler(int irq, void *arg) +{ + struct eventfd_ctx *trigger = arg; + + eventfd_signal(trigger, 1); + return IRQ_HANDLED; +} + +static int do_set_irq_trigger(struct dfl_feature *feature, unsigned int idx, + int fd) +{ + struct platform_device *pdev = feature->dev; + struct eventfd_ctx *trigger; + int irq, ret; + + irq = feature->irq_ctx[idx].irq; + + if (feature->irq_ctx[idx].trigger) { + free_irq(irq, feature->irq_ctx[idx].trigger); + kfree(feature->irq_ctx[idx].name); + eventfd_ctx_put(feature->irq_ctx[idx].trigger); + feature->irq_ctx[idx].trigger = NULL; + } + + if (fd < 0) + return 0; + + feature->irq_ctx[idx].name = + kasprintf(GFP_KERNEL, "fpga-irq[%u](%s-%llx)", idx, + dev_name(&pdev->dev), feature->id); + if (!feature->irq_ctx[idx].name) + return -ENOMEM; + + trigger = eventfd_ctx_fdget(fd); + if (IS_ERR(trigger)) { + ret = PTR_ERR(trigger); + goto free_name; + } + + ret = request_irq(irq, dfl_irq_handler, 0, + feature->irq_ctx[idx].name, trigger); + if (!ret) { + feature->irq_ctx[idx].trigger = trigger; + return ret; + } + + eventfd_ctx_put(trigger); +free_name: + kfree(feature->irq_ctx[idx].name); + + return ret; +} + +/** + * dfl_fpga_set_irq_triggers - set eventfd triggers for dfl feature interrupts + * + * @feature: dfl sub feature. + * @start: start of irq index in this dfl sub feature. + * @count: number of irqs. + * @fds: eventfds to bind with irqs. unbind related irq if fds[n] is negative. + * unbind "count" specified number of irqs if fds ptr is NULL. + * + * Bind given eventfds with irqs in this dfl sub feature. Unbind related irq if + * fds[n] is negative. Unbind "count" specified number of irqs if fds ptr is + * NULL. + * + * Return: 0 on success, negative error code otherwise. + */ +int dfl_fpga_set_irq_triggers(struct dfl_feature *feature, unsigned int start, + unsigned int count, int32_t *fds) +{ + unsigned int i; + int ret = 0; + + /* overflow */ + if (unlikely(start + count < start)) + return -EINVAL; + + /* exceeds nr_irqs */ + if (start + count > feature->nr_irqs) + return -EINVAL; + + for (i = 0; i < count; i++) { + int fd = fds ? fds[i] : -1; + + ret = do_set_irq_trigger(feature, start + i, fd); + if (ret) { + while (i--) + do_set_irq_trigger(feature, start + i, -1); + break; + } + } + + return ret; +} +EXPORT_SYMBOL_GPL(dfl_fpga_set_irq_triggers); + +/** + * dfl_feature_ioctl_get_num_irqs - dfl feature _GET_IRQ_NUM ioctl interface. + * @pdev: the feature device which has the sub feature + * @feature: the dfl sub feature + * @arg: ioctl argument + * + * Return: 0 on success, negative error code otherwise. + */ +long dfl_feature_ioctl_get_num_irqs(struct platform_device *pdev, + struct dfl_feature *feature, + unsigned long arg) +{ + return put_user(feature->nr_irqs, (__u32 __user *)arg); +} +EXPORT_SYMBOL_GPL(dfl_feature_ioctl_get_num_irqs); + +/** + * dfl_feature_ioctl_set_irq - dfl feature _SET_IRQ ioctl interface. + * @pdev: the feature device which has the sub feature + * @feature: the dfl sub feature + * @arg: ioctl argument + * + * Return: 0 on success, negative error code otherwise. + */ +long dfl_feature_ioctl_set_irq(struct platform_device *pdev, + struct dfl_feature *feature, + unsigned long arg) +{ + struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct dfl_fpga_irq_set hdr; + s32 *fds; + long ret; + + if (!feature->nr_irqs) + return -ENOENT; + + if (copy_from_user(&hdr, (void __user *)arg, sizeof(hdr))) + return -EFAULT; + + if (!hdr.count || (hdr.start + hdr.count > feature->nr_irqs) || + (hdr.start + hdr.count < hdr.start)) + return -EINVAL; + + fds = memdup_user((void __user *)(arg + sizeof(hdr)), + hdr.count * sizeof(s32)); + if (IS_ERR(fds)) + return PTR_ERR(fds); + + mutex_lock(&pdata->lock); + ret = dfl_fpga_set_irq_triggers(feature, hdr.start, hdr.count, fds); + mutex_unlock(&pdata->lock); + + kfree(fds); + return ret; +} +EXPORT_SYMBOL_GPL(dfl_feature_ioctl_set_irq); + static void __exit dfl_fpga_exit(void) { dfl_chardev_uinit(); diff --git a/drivers/fpga/dfl.h b/drivers/fpga/dfl.h index e1f3ab86560e..a32dfba2a88b 100644 --- a/drivers/fpga/dfl.h +++ b/drivers/fpga/dfl.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -214,14 +215,19 @@ struct dfl_feature_driver { * struct dfl_feature_irq_ctx - dfl private feature interrupt context * * @irq: Linux IRQ number of this interrupt. + * @trigger: eventfd context to signal when interrupt happens. + * @name: irq name needed when requesting irq. */ struct dfl_feature_irq_ctx { int irq; + struct eventfd_ctx *trigger; + char *name; }; /** * struct dfl_feature - sub feature of the feature devices * + * @dev: ptr to pdev of the feature device which has the sub feature. * @id: sub feature id. * @resource_index: each sub feature has one mmio resource for its registers. * this index is used to find its mmio resource from the @@ -233,6 +239,7 @@ struct dfl_feature_irq_ctx { * @priv: priv data of this feature. */ struct dfl_feature { + struct platform_device *dev; u64 id; int resource_index; void __iomem *ioaddr; @@ -503,4 +510,13 @@ int dfl_fpga_cdev_release_port(struct dfl_fpga_cdev *cdev, int port_id); int dfl_fpga_cdev_assign_port(struct dfl_fpga_cdev *cdev, int port_id); void dfl_fpga_cdev_config_ports_pf(struct dfl_fpga_cdev *cdev); int dfl_fpga_cdev_config_ports_vf(struct dfl_fpga_cdev *cdev, int num_vf); +int dfl_fpga_set_irq_triggers(struct dfl_feature *feature, unsigned int start, + unsigned int count, int32_t *fds); +long dfl_feature_ioctl_get_num_irqs(struct platform_device *pdev, + struct dfl_feature *feature, + unsigned long arg); +long dfl_feature_ioctl_set_irq(struct platform_device *pdev, + struct dfl_feature *feature, + unsigned long arg); + #endif /* __FPGA_DFL_H */ diff --git a/include/uapi/linux/fpga-dfl.h b/include/uapi/linux/fpga-dfl.h index ec70a0746e59..7331350f3067 100644 --- a/include/uapi/linux/fpga-dfl.h +++ b/include/uapi/linux/fpga-dfl.h @@ -151,6 +151,19 @@ struct dfl_fpga_port_dma_unmap { #define DFL_FPGA_PORT_DMA_UNMAP _IO(DFL_FPGA_MAGIC, DFL_PORT_BASE + 4) +/** + * struct dfl_fpga_irq_set - the argument for DFL_FPGA_XXX_SET_IRQ ioctl. + * + * @start: Index of the first irq. + * @count: The number of eventfd handler. + * @evtfds: Eventfd handlers. + */ +struct dfl_fpga_irq_set { + __u32 start; + __u32 count; + __s32 evtfds[]; +}; + /* IOCTLs for FME file descriptor */ /** -- cgit v1.2.3-59-g8ed1b From 5dcbc71126e1dc41e32810af3f24b4a318eba5b3 Mon Sep 17 00:00:00 2001 From: Jonathan Marek Date: Sat, 23 May 2020 22:14:13 -0400 Subject: phy: qcom-qmp: Allow different values for second lane The primary USB PHY on sm8250 sets some values differently for the second lane. This makes it possible to represent that. Signed-off-by: Jonathan Marek Tested-by: Dmitry Baryshkov Reviewed-by: Bjorn Andersson Link: https://lore.kernel.org/r/20200524021416.17049-2-jonathan@marek.ca Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-qmp.c | 52 ++++++++++++++++++++++++++++--------- 1 file changed, 40 insertions(+), 12 deletions(-) diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.c b/drivers/phy/qualcomm/phy-qcom-qmp.c index 544f140553d4..058650b0f018 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp.c @@ -82,20 +82,34 @@ struct qmp_phy_init_tbl { * register part of layout ? * if yes, then offset gives index in the reg-layout */ - int in_layout; + bool in_layout; + /* + * mask of lanes for which this register is written + * for cases when second lane needs different values + */ + u8 lane_mask; }; #define QMP_PHY_INIT_CFG(o, v) \ { \ .offset = o, \ .val = v, \ + .lane_mask = 0xff, \ } #define QMP_PHY_INIT_CFG_L(o, v) \ { \ .offset = o, \ .val = v, \ - .in_layout = 1, \ + .in_layout = true, \ + .lane_mask = 0xff, \ + } + +#define QMP_PHY_INIT_CFG_LANE(o, v, l) \ + { \ + .offset = o, \ + .val = v, \ + .lane_mask = l, \ } /* set of registers with offsets different per-PHY */ @@ -2085,10 +2099,11 @@ static const struct qmp_phy_cfg sm8150_usb3phy_cfg = { .is_dual_lane_phy = true, }; -static void qcom_qmp_phy_configure(void __iomem *base, - const unsigned int *regs, - const struct qmp_phy_init_tbl tbl[], - int num) +static void qcom_qmp_phy_configure_lane(void __iomem *base, + const unsigned int *regs, + const struct qmp_phy_init_tbl tbl[], + int num, + u8 lane_mask) { int i; const struct qmp_phy_init_tbl *t = tbl; @@ -2097,6 +2112,9 @@ static void qcom_qmp_phy_configure(void __iomem *base, return; for (i = 0; i < num; i++, t++) { + if (!(t->lane_mask & lane_mask)) + continue; + if (t->in_layout) writel(t->val, base + regs[t->offset]); else @@ -2104,6 +2122,14 @@ static void qcom_qmp_phy_configure(void __iomem *base, } } +static void qcom_qmp_phy_configure(void __iomem *base, + const unsigned int *regs, + const struct qmp_phy_init_tbl tbl[], + int num) +{ + qcom_qmp_phy_configure_lane(base, regs, tbl, num, 0xff); +} + static int qcom_qmp_phy_com_init(struct qmp_phy *qphy) { struct qcom_qmp *qmp = qphy->qmp; @@ -2318,16 +2344,18 @@ static int qcom_qmp_phy_enable(struct phy *phy) } /* Tx, Rx, and PCS configurations */ - qcom_qmp_phy_configure(tx, cfg->regs, cfg->tx_tbl, cfg->tx_tbl_num); + qcom_qmp_phy_configure_lane(tx, cfg->regs, + cfg->tx_tbl, cfg->tx_tbl_num, 1); /* Configuration for other LANE for USB-DP combo PHY */ if (cfg->is_dual_lane_phy) - qcom_qmp_phy_configure(qphy->tx2, cfg->regs, - cfg->tx_tbl, cfg->tx_tbl_num); + qcom_qmp_phy_configure_lane(qphy->tx2, cfg->regs, + cfg->tx_tbl, cfg->tx_tbl_num, 2); - qcom_qmp_phy_configure(rx, cfg->regs, cfg->rx_tbl, cfg->rx_tbl_num); + qcom_qmp_phy_configure_lane(rx, cfg->regs, + cfg->rx_tbl, cfg->rx_tbl_num, 1); if (cfg->is_dual_lane_phy) - qcom_qmp_phy_configure(qphy->rx2, cfg->regs, - cfg->rx_tbl, cfg->rx_tbl_num); + qcom_qmp_phy_configure_lane(qphy->rx2, cfg->regs, + cfg->rx_tbl, cfg->rx_tbl_num, 2); qcom_qmp_phy_configure(pcs, cfg->regs, cfg->pcs_tbl, cfg->pcs_tbl_num); ret = reset_control_deassert(qmp->ufs_reset); -- cgit v1.2.3-59-g8ed1b From 7b675ba1d27854abde20c52c47f0957756e04304 Mon Sep 17 00:00:00 2001 From: Jonathan Marek Date: Sat, 23 May 2020 22:14:14 -0400 Subject: phy: qcom-qmp: Add QMP V4 USB3 UNIPHY Add support for the USB3 PHY used by the secondary usb controller on sm8150 Signed-off-by: Jonathan Marek Tested-by: Dmitry Baryshkov Reviewed-by: Bjorn Andersson Link: https://lore.kernel.org/r/20200524021416.17049-3-jonathan@marek.ca Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-qmp.c | 150 ++++++++++++++++++++++++++++++++++++ drivers/phy/qualcomm/phy-qcom-qmp.h | 5 ++ 2 files changed, 155 insertions(+) diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.c b/drivers/phy/qualcomm/phy-qcom-qmp.c index 058650b0f018..753997db0317 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp.c @@ -199,6 +199,17 @@ static const unsigned int qmp_v4_usb3phy_regs_layout[QPHY_LAYOUT_SIZE] = { [QPHY_START_CTRL] = 0x44, [QPHY_PCS_STATUS] = 0x14, [QPHY_PCS_POWER_DOWN_CONTROL] = 0x40, + [QPHY_PCS_AUTONOMOUS_MODE_CTRL] = 0x308, + [QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR] = 0x314, +}; + +static const unsigned int qmp_v4_usb3_uniphy_regs_layout[QPHY_LAYOUT_SIZE] = { + [QPHY_SW_RESET] = 0x00, + [QPHY_START_CTRL] = 0x44, + [QPHY_PCS_STATUS] = 0x14, + [QPHY_PCS_POWER_DOWN_CONTROL] = 0x40, + [QPHY_PCS_AUTONOMOUS_MODE_CTRL] = 0x608, + [QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR] = 0x614, }; static const unsigned int sdm845_ufsphy_regs_layout[QPHY_LAYOUT_SIZE] = { @@ -1488,6 +1499,114 @@ static const struct qmp_phy_init_tbl sm8150_usb3_pcs_tbl[] = { QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_RXEQTRAINING_DFE_TIME_S2, 0x07), }; +static const struct qmp_phy_init_tbl sm8150_usb3_uniphy_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_EN_SEL, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_HSCLK_SEL, 0x11), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_SEL, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE0, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START1_MODE0, 0xab), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START2_MODE0, 0xea), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START3_MODE0, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0xca), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x1e), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE0, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE0, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE1_MODE0, 0x24), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE0, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE0, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP_EN, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_BUF_ENABLE, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE2_MODE1, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE1_MODE1, 0x24), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CORECLK_DIV_MODE1, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE1, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START1_MODE1, 0xab), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START2_MODE1, 0xea), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START3_MODE1, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE1, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE1, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE1, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE1, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE1, 0xca), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE1, 0x1e), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CMN_IPTRIM, 0x20), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_EN_CENTER, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_PER1, 0x31), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_PER2, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE1_MODE1, 0xde), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE2_MODE1, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE1_MODE0, 0xde), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE2_MODE0, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE_MAP, 0x02), +}; + +static const struct qmp_phy_init_tbl sm8150_usb3_uniphy_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RCV_DETECT_LVL_2, 0x12), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_LANE_MODE_1, 0x95), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_PI_QEC_CTRL, 0x40), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_OFFSET_TX, 0x05), +}; + +static const struct qmp_phy_init_tbl sm8150_usb3_uniphy_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH4, 0xb8), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH3, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH2, 0x37), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH, 0x2f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_LOW, 0xef), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH4, 0xb3), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH3, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH2, 0x5c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH, 0xdc), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_LOW, 0xdc), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_PI_CONTROLS, 0x99), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_THRESH1, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_THRESH2, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_GAIN1, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_GAIN2, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_FO_GAIN, 0x2f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_LOW, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FO_GAIN, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL1, 0x54), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL2, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_GM_CAL, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4a), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_EN_TIMER, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x47), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_CNTRL, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_DEGLITCH_CNTRL, 0x0e), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_HIGH, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_LOW, 0xc0), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_CTLE_POST_CAL_OFFSET, 0x20), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_GAIN, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DCC_CTRL1, 0x0c), +}; + +static const struct qmp_phy_init_tbl sm8150_usb3_uniphy_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG1, 0xd0), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG2, 0x07), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG3, 0x20), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG6, 0x13), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_RCVR_DTCT_DLY_P1U2_L, 0xe7), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_RCVR_DTCT_DLY_P1U2_H, 0x03), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_RX_SIGDET_LVL, 0xaa), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_UNI_RXEQTRAINING_DFE_TIME_S2, 0x07), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_UNI_LFPS_DET_HIGH_COUNT_VAL, 0xf8), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_CDR_RESET_TIME, 0x0f), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG1, 0x88), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG2, 0x13), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG1, 0x4b), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG5, 0x10), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_REFGEN_REQ_CONFIG1, 0x21), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCS_TX_RX_CONFIG, 0x0c), +}; + /* struct qmp_phy_cfg - per-PHY initialization config */ struct qmp_phy_cfg { /* phy-type - PCIE/UFS/USB */ @@ -2099,6 +2218,34 @@ static const struct qmp_phy_cfg sm8150_usb3phy_cfg = { .is_dual_lane_phy = true, }; +static const struct qmp_phy_cfg sm8150_usb3_uniphy_cfg = { + .type = PHY_TYPE_USB3, + .nlanes = 1, + + .serdes_tbl = sm8150_usb3_uniphy_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(sm8150_usb3_uniphy_serdes_tbl), + .tx_tbl = sm8150_usb3_uniphy_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(sm8150_usb3_uniphy_tx_tbl), + .rx_tbl = sm8150_usb3_uniphy_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(sm8150_usb3_uniphy_rx_tbl), + .pcs_tbl = sm8150_usb3_uniphy_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(sm8150_usb3_uniphy_pcs_tbl), + .clk_list = qmp_v4_phy_clk_l, + .num_clks = ARRAY_SIZE(qmp_v4_phy_clk_l), + .reset_list = msm8996_usb3phy_reset_l, + .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = qmp_v4_usb3_uniphy_regs_layout, + + .start_ctrl = SERDES_START | PCS_START, + .pwrdn_ctrl = SW_PWRDN, + + .has_pwrdn_delay = true, + .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN, + .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX, +}; + static void qcom_qmp_phy_configure_lane(void __iomem *base, const unsigned int *regs, const struct qmp_phy_init_tbl tbl[], @@ -2876,6 +3023,9 @@ static const struct of_device_id qcom_qmp_phy_of_match_table[] = { }, { .compatible = "qcom,sm8150-qmp-usb3-phy", .data = &sm8150_usb3phy_cfg, + }, { + .compatible = "qcom,sm8150-qmp-usb3-uni-phy", + .data = &sm8150_usb3_uniphy_cfg, }, { }, }; diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.h b/drivers/phy/qualcomm/phy-qcom-qmp.h index 6d017a0c0c8d..f39f7a968228 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.h +++ b/drivers/phy/qualcomm/phy-qcom-qmp.h @@ -363,6 +363,7 @@ /* Only for QMP V4 PHY - TX registers */ #define QSERDES_V4_TX_RES_CODE_LANE_TX 0x34 #define QSERDES_V4_TX_RES_CODE_LANE_RX 0x38 +#define QSERDES_V4_TX_RES_CODE_LANE_OFFSET_TX 0x3c #define QSERDES_V4_TX_LANE_MODE_1 0x84 #define QSERDES_V4_TX_RCV_DETECT_LVL_2 0x9c #define QSERDES_V4_TX_PWM_GEAR_1_DIVIDER_BAND0_1 0xd8 @@ -709,6 +710,10 @@ #define QPHY_V4_PCS_USB3_SIGDET_STARTUP_TIMER_VAL 0x354 #define QPHY_V4_PCS_USB3_TEST_CONTROL 0x358 +/* Only for QMP V4 PHY - UNI has 0x300 offset for PCS_USB3 regs */ +#define QPHY_V4_PCS_USB3_UNI_LFPS_DET_HIGH_COUNT_VAL 0x618 +#define QPHY_V4_PCS_USB3_UNI_RXEQTRAINING_DFE_TIME_S2 0x638 + /* Only for QMP V4 PHY - PCS_MISC registers */ #define QPHY_V4_PCS_MISC_TYPEC_CTRL 0x00 #define QPHY_V4_PCS_MISC_TYPEC_PWRDN_CTRL 0x04 -- cgit v1.2.3-59-g8ed1b From 90b65347cfc5c812b4c3fba4c5f55cbb2c19d286 Mon Sep 17 00:00:00 2001 From: Jonathan Marek Date: Sat, 23 May 2020 22:14:15 -0400 Subject: phy: qcom-qmp: Add QMP V4 USB3 PHY support for sm8250 Add both the DP and UNI PHY for primary/secondary usb controllers. The tables are very similar to sm8150 (serdes_tbl is identical), but there are some differences. Signed-off-by: Jonathan Marek Tested-by: Dmitry Baryshkov Reviewed-by: Bjorn Andersson Link: https://lore.kernel.org/r/20200524021416.17049-4-jonathan@marek.ca Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-qmp.c | 206 ++++++++++++++++++++++++++++++++++++ drivers/phy/qualcomm/phy-qcom-qmp.h | 2 + 2 files changed, 208 insertions(+) diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.c b/drivers/phy/qualcomm/phy-qcom-qmp.c index 753997db0317..562053ce9455 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp.c @@ -1607,6 +1607,142 @@ static const struct qmp_phy_init_tbl sm8150_usb3_uniphy_pcs_tbl[] = { QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCS_TX_RX_CONFIG, 0x0c), }; +static const struct qmp_phy_init_tbl sm8250_usb3_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_TX, 0x60), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_RX, 0x60), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_OFFSET_TX, 0x11), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_OFFSET_RX, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_LANE_MODE_1, 0xd5), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RCV_DETECT_LVL_2, 0x12), + QMP_PHY_INIT_CFG_LANE(QSERDES_V4_TX_PI_QEC_CTRL, 0x40, 1), + QMP_PHY_INIT_CFG_LANE(QSERDES_V4_TX_PI_QEC_CTRL, 0x54, 2), +}; + +static const struct qmp_phy_init_tbl sm8250_usb3_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_GAIN, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_FO_GAIN, 0x2f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_LOW, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_PI_CONTROLS, 0x99), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_THRESH1, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_THRESH2, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_GAIN1, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_GAIN2, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL1, 0x54), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL2, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4a), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_LOW, 0xc0), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_HIGH, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_CNTRL, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_DEGLITCH_CNTRL, 0x0e), + QMP_PHY_INIT_CFG_LANE(QSERDES_V4_RX_RX_MODE_00_LOW, 0xff, 1), + QMP_PHY_INIT_CFG_LANE(QSERDES_V4_RX_RX_MODE_00_LOW, 0x7f, 2), + QMP_PHY_INIT_CFG_LANE(QSERDES_V4_RX_RX_MODE_00_HIGH, 0x7f, 1), + QMP_PHY_INIT_CFG_LANE(QSERDES_V4_RX_RX_MODE_00_HIGH, 0xff, 2), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH2, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH3, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH4, 0x97), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_LOW, 0xdc), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH, 0xdc), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH2, 0x5c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH3, 0x7b), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH4, 0xb4), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_EN_TIMER, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_AUX_DATA_TCOARSE_TFINE, 0xa0), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DCC_CTRL1, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_GM_CAL, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_VTH_CODE, 0x10), +}; + +static const struct qmp_phy_init_tbl sm8250_usb3_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG1, 0xd0), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG2, 0x07), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG3, 0x20), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG6, 0x13), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_REFGEN_REQ_CONFIG1, 0x21), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_RX_SIGDET_LVL, 0xa9), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_CDR_RESET_TIME, 0x0a), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG1, 0x88), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG2, 0x13), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCS_TX_RX_CONFIG, 0x0c), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG1, 0x4b), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG5, 0x10), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_LFPS_DET_HIGH_COUNT_VAL, 0xf8), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_RXEQTRAINING_DFE_TIME_S2, 0x07), +}; + +static const struct qmp_phy_init_tbl sm8250_usb3_uniphy_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RCV_DETECT_LVL_2, 0x12), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_LANE_MODE_1, 0xd5), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_LANE_MODE_2, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_PI_QEC_CTRL, 0x40), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_OFFSET_TX, 0x11), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_OFFSET_RX, 0x02), +}; + +static const struct qmp_phy_init_tbl sm8250_usb3_uniphy_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH4, 0xb8), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH3, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH2, 0xbf), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_LOW, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH4, 0xb4), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH3, 0x7b), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH2, 0x5c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH, 0xdc), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_LOW, 0xdc), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_PI_CONTROLS, 0x99), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_THRESH1, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_THRESH2, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_GAIN1, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_GAIN2, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_FO_GAIN, 0x2f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_LOW, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FO_GAIN, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL1, 0x54), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL2, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4a), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_EN_TIMER, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x47), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_CNTRL, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_DEGLITCH_CNTRL, 0x0e), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_HIGH, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_LOW, 0xc0), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_GAIN, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DCC_CTRL1, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_GM_CAL, 0x1f), +}; + +static const struct qmp_phy_init_tbl sm8250_usb3_uniphy_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG1, 0xd0), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG2, 0x07), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG3, 0x20), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG6, 0x13), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_RCVR_DTCT_DLY_P1U2_L, 0xe7), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_RCVR_DTCT_DLY_P1U2_H, 0x03), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_RX_SIGDET_LVL, 0xa9), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCS_TX_RX_CONFIG, 0x0c), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_UNI_RXEQTRAINING_DFE_TIME_S2, 0x07), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_UNI_LFPS_DET_HIGH_COUNT_VAL, 0xf8), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_CDR_RESET_TIME, 0x0a), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG1, 0x88), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG2, 0x13), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG1, 0x4b), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG5, 0x10), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_REFGEN_REQ_CONFIG1, 0x21), +}; + /* struct qmp_phy_cfg - per-PHY initialization config */ struct qmp_phy_cfg { /* phy-type - PCIE/UFS/USB */ @@ -1775,6 +1911,11 @@ static const char * const qmp_v4_phy_clk_l[] = { "aux", "ref_clk_src", "ref", "com_aux", }; +/* the primary usb3 phy on sm8250 doesn't have a ref clock */ +static const char * const qmp_v4_sm8250_usbphy_clk_l[] = { + "aux", "ref_clk_src", "com_aux" +}; + static const char * const sdm845_ufs_phy_clk_l[] = { "ref", "ref_aux", }; @@ -2246,6 +2387,65 @@ static const struct qmp_phy_cfg sm8150_usb3_uniphy_cfg = { .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX, }; +static const struct qmp_phy_cfg sm8250_usb3phy_cfg = { + .type = PHY_TYPE_USB3, + .nlanes = 1, + + .serdes_tbl = sm8150_usb3_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(sm8150_usb3_serdes_tbl), + .tx_tbl = sm8250_usb3_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(sm8250_usb3_tx_tbl), + .rx_tbl = sm8250_usb3_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(sm8250_usb3_rx_tbl), + .pcs_tbl = sm8250_usb3_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(sm8250_usb3_pcs_tbl), + .clk_list = qmp_v4_sm8250_usbphy_clk_l, + .num_clks = ARRAY_SIZE(qmp_v4_sm8250_usbphy_clk_l), + .reset_list = msm8996_usb3phy_reset_l, + .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = qmp_v4_usb3phy_regs_layout, + + .start_ctrl = SERDES_START | PCS_START, + .pwrdn_ctrl = SW_PWRDN, + + .has_pwrdn_delay = true, + .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN, + .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX, + + .has_phy_dp_com_ctrl = true, + .is_dual_lane_phy = true, +}; + +static const struct qmp_phy_cfg sm8250_usb3_uniphy_cfg = { + .type = PHY_TYPE_USB3, + .nlanes = 1, + + .serdes_tbl = sm8150_usb3_uniphy_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(sm8150_usb3_uniphy_serdes_tbl), + .tx_tbl = sm8250_usb3_uniphy_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(sm8250_usb3_uniphy_tx_tbl), + .rx_tbl = sm8250_usb3_uniphy_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(sm8250_usb3_uniphy_rx_tbl), + .pcs_tbl = sm8250_usb3_uniphy_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(sm8250_usb3_uniphy_pcs_tbl), + .clk_list = qmp_v4_phy_clk_l, + .num_clks = ARRAY_SIZE(qmp_v4_phy_clk_l), + .reset_list = msm8996_usb3phy_reset_l, + .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = qmp_v4_usb3_uniphy_regs_layout, + + .start_ctrl = SERDES_START | PCS_START, + .pwrdn_ctrl = SW_PWRDN, + + .has_pwrdn_delay = true, + .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN, + .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX, +}; + static void qcom_qmp_phy_configure_lane(void __iomem *base, const unsigned int *regs, const struct qmp_phy_init_tbl tbl[], @@ -3026,6 +3226,12 @@ static const struct of_device_id qcom_qmp_phy_of_match_table[] = { }, { .compatible = "qcom,sm8150-qmp-usb3-uni-phy", .data = &sm8150_usb3_uniphy_cfg, + }, { + .compatible = "qcom,sm8250-qmp-usb3-phy", + .data = &sm8250_usb3phy_cfg, + }, { + .compatible = "qcom,sm8250-qmp-usb3-uni-phy", + .data = &sm8250_usb3_uniphy_cfg, }, { }, }; diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.h b/drivers/phy/qualcomm/phy-qcom-qmp.h index f39f7a968228..4277f592684b 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.h +++ b/drivers/phy/qualcomm/phy-qcom-qmp.h @@ -364,7 +364,9 @@ #define QSERDES_V4_TX_RES_CODE_LANE_TX 0x34 #define QSERDES_V4_TX_RES_CODE_LANE_RX 0x38 #define QSERDES_V4_TX_RES_CODE_LANE_OFFSET_TX 0x3c +#define QSERDES_V4_TX_RES_CODE_LANE_OFFSET_RX 0x40 #define QSERDES_V4_TX_LANE_MODE_1 0x84 +#define QSERDES_V4_TX_LANE_MODE_2 0x88 #define QSERDES_V4_TX_RCV_DETECT_LVL_2 0x9c #define QSERDES_V4_TX_PWM_GEAR_1_DIVIDER_BAND0_1 0xd8 #define QSERDES_V4_TX_PWM_GEAR_2_DIVIDER_BAND0_1 0xdC -- cgit v1.2.3-59-g8ed1b From 0d75f508a9d56133a2834215959cde4605c804d1 Mon Sep 17 00:00:00 2001 From: Wesley Cheng Date: Thu, 25 Jun 2020 12:54:43 -0700 Subject: phy: qcom-snps: Add runtime suspend and resume handlers Allow for the PHY to be put into a powered down state when possible. Add the required suspend and resume callbacks, which will determine what resources can be turned off depending on the cable status. Signed-off-by: Wesley Cheng Link: https://lore.kernel.org/r/20200625195444.15130-2-wcheng@codeaurora.org Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c | 73 +++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c b/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c index 4d74045271eb..152d8633f4ea 100644 --- a/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c +++ b/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c @@ -104,6 +104,63 @@ static inline void qcom_snps_hsphy_write_mask(void __iomem *base, u32 offset, readl_relaxed(base + offset); } +static int qcom_snps_hsphy_suspend(struct qcom_snps_hsphy *hsphy) +{ + dev_dbg(&hsphy->phy->dev, "Suspend QCOM SNPS PHY\n"); + + if (hsphy->mode == PHY_MODE_USB_HOST) { + /* Enable auto-resume to meet remote wakeup timing */ + qcom_snps_hsphy_write_mask(hsphy->base, + USB2_PHY_USB_PHY_HS_PHY_CTRL2, + USB2_AUTO_RESUME, + USB2_AUTO_RESUME); + usleep_range(500, 1000); + qcom_snps_hsphy_write_mask(hsphy->base, + USB2_PHY_USB_PHY_HS_PHY_CTRL2, + 0, USB2_AUTO_RESUME); + } + + clk_disable_unprepare(hsphy->cfg_ahb_clk); + return 0; +} + +static int qcom_snps_hsphy_resume(struct qcom_snps_hsphy *hsphy) +{ + int ret; + + dev_dbg(&hsphy->phy->dev, "Resume QCOM SNPS PHY, mode\n"); + + ret = clk_prepare_enable(hsphy->cfg_ahb_clk); + if (ret) { + dev_err(&hsphy->phy->dev, "failed to enable cfg ahb clock\n"); + return ret; + } + + return 0; +} + +static int __maybe_unused qcom_snps_hsphy_runtime_suspend(struct device *dev) +{ + struct qcom_snps_hsphy *hsphy = dev_get_drvdata(dev); + + if (!hsphy->phy_initialized) + return 0; + + qcom_snps_hsphy_suspend(hsphy); + return 0; +} + +static int __maybe_unused qcom_snps_hsphy_runtime_resume(struct device *dev) +{ + struct qcom_snps_hsphy *hsphy = dev_get_drvdata(dev); + + if (!hsphy->phy_initialized) + return 0; + + qcom_snps_hsphy_resume(hsphy); + return 0; +} + static int qcom_snps_hsphy_init(struct phy *phy) { struct qcom_snps_hsphy *hsphy = phy_get_drvdata(phy); @@ -212,6 +269,11 @@ static const struct of_device_id qcom_snps_hsphy_of_match_table[] = { }; MODULE_DEVICE_TABLE(of, qcom_snps_hsphy_of_match_table); +static const struct dev_pm_ops qcom_snps_hsphy_pm_ops = { + SET_RUNTIME_PM_OPS(qcom_snps_hsphy_runtime_suspend, + qcom_snps_hsphy_runtime_resume, NULL) +}; + static int qcom_snps_hsphy_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -255,6 +317,14 @@ static int qcom_snps_hsphy_probe(struct platform_device *pdev) return ret; } + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + /* + * Prevent runtime pm from being ON by default. Users can enable + * it using power/control in sysfs. + */ + pm_runtime_forbid(dev); + generic_phy = devm_phy_create(dev, NULL, &qcom_snps_hsphy_gen_ops); if (IS_ERR(generic_phy)) { ret = PTR_ERR(generic_phy); @@ -269,6 +339,8 @@ static int qcom_snps_hsphy_probe(struct platform_device *pdev) phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); if (!IS_ERR(phy_provider)) dev_dbg(dev, "Registered Qcom-SNPS HS phy\n"); + else + pm_runtime_disable(dev); return PTR_ERR_OR_ZERO(phy_provider); } @@ -277,6 +349,7 @@ static struct platform_driver qcom_snps_hsphy_driver = { .probe = qcom_snps_hsphy_probe, .driver = { .name = "qcom-snps-hs-femto-v2-phy", + .pm = &qcom_snps_hsphy_pm_ops, .of_match_table = qcom_snps_hsphy_of_match_table, }, }; -- cgit v1.2.3-59-g8ed1b From dcbec046507615d7c4b5f6682dc11a1be9a2924c Mon Sep 17 00:00:00 2001 From: Wesley Cheng Date: Thu, 25 Jun 2020 12:54:44 -0700 Subject: phy: qcom-snps: Add a set mode callback The set mode handler is used to keep track of the current role of the device. This is used for enabling certain resources within the PHY depending on if the device is behaving as a host or device. Signed-off-by: Wesley Cheng Link: https://lore.kernel.org/r/20200625195444.15130-3-wcheng@codeaurora.org Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c b/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c index 152d8633f4ea..ae4bac024c7b 100644 --- a/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c +++ b/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c @@ -77,6 +77,7 @@ static const char * const qcom_snps_hsphy_vreg_names[] = { * @phy_reset: phy reset control * @vregs: regulator supplies bulk data * @phy_initialized: if PHY has been initialized correctly + * @mode: contains the current mode the PHY is in */ struct qcom_snps_hsphy { struct phy *phy; @@ -88,6 +89,7 @@ struct qcom_snps_hsphy { struct regulator_bulk_data vregs[SNPS_HS_NUM_VREGS]; bool phy_initialized; + enum phy_mode mode; }; static inline void qcom_snps_hsphy_write_mask(void __iomem *base, u32 offset, @@ -161,6 +163,15 @@ static int __maybe_unused qcom_snps_hsphy_runtime_resume(struct device *dev) return 0; } +static int qcom_snps_hsphy_set_mode(struct phy *phy, enum phy_mode mode, + int submode) +{ + struct qcom_snps_hsphy *hsphy = phy_get_drvdata(phy); + + hsphy->mode = mode; + return 0; +} + static int qcom_snps_hsphy_init(struct phy *phy) { struct qcom_snps_hsphy *hsphy = phy_get_drvdata(phy); @@ -258,6 +269,7 @@ static int qcom_snps_hsphy_exit(struct phy *phy) static const struct phy_ops qcom_snps_hsphy_gen_ops = { .init = qcom_snps_hsphy_init, .exit = qcom_snps_hsphy_exit, + .set_mode = qcom_snps_hsphy_set_mode, .owner = THIS_MODULE, }; -- cgit v1.2.3-59-g8ed1b From cea0f76a483d1270ac6f6513964e3e75193dda48 Mon Sep 17 00:00:00 2001 From: Anurag Kumar Vulisha Date: Mon, 29 Jun 2020 15:00:52 +0300 Subject: dt-bindings: phy: Add DT bindings for Xilinx ZynqMP PSGTR PHY Add DT bindings for the Xilinx ZynqMP PHY. ZynqMP SoCs have a High Speed Processing System Gigabit Transceiver which provides PHY capabilities to USB, SATA, PCIE, Display Port and Ehernet SGMII controllers. Signed-off-by: Anurag Kumar Vulisha Signed-off-by: Laurent Pinchart Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20200629120054.29338-2-laurent.pinchart@ideasonboard.com Signed-off-by: Vinod Koul --- .../devicetree/bindings/phy/xlnx,zynqmp-psgtr.yaml | 105 +++++++++++++++++++++ include/dt-bindings/phy/phy.h | 1 + 2 files changed, 106 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/xlnx,zynqmp-psgtr.yaml diff --git a/Documentation/devicetree/bindings/phy/xlnx,zynqmp-psgtr.yaml b/Documentation/devicetree/bindings/phy/xlnx,zynqmp-psgtr.yaml new file mode 100644 index 000000000000..09e3cde7ebca --- /dev/null +++ b/Documentation/devicetree/bindings/phy/xlnx,zynqmp-psgtr.yaml @@ -0,0 +1,105 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/xlnx,zynqmp-psgtr.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Xilinx ZynqMP Gigabit Transceiver PHY Device Tree Bindings + +maintainers: + - Laurent Pinchart + +description: | + This binding describes the Xilinx ZynqMP Gigabit Transceiver (GTR) PHY. The + GTR provides four lanes and is used by USB, SATA, PCIE, Display port and + Ethernet SGMII controllers. + +properties: + "#phy-cells": + const: 4 + description: | + The cells contain the following arguments. + + - description: The GTR lane + minimum: 0 + maximum: 3 + - description: The PHY type + enum: + - PHY_TYPE_DP + - PHY_TYPE_PCIE + - PHY_TYPE_SATA + - PHY_TYPE_SGMII + - PHY_TYPE_USB + - description: The PHY instance + minimum: 0 + maximum: 1 # for DP, SATA or USB + maximum: 3 # for PCIE or SGMII + - description: The reference clock number + minimum: 0 + maximum: 3 + + compatible: + enum: + - xlnx,zynqmp-psgtr-v1.1 + - xlnx,zynqmp-psgtr + + clocks: + minItems: 1 + maxItems: 4 + description: | + Clock for each PS_MGTREFCLK[0-3] reference clock input. Unconnected + inputs shall not have an entry. + + clock-names: + minItems: 1 + maxItems: 4 + items: + pattern: "^ref[0-3]$" + + reg: + items: + - description: SERDES registers block + - description: SIOU registers block + + reg-names: + items: + - const: serdes + - const: siou + + xlnx,tx-termination-fix: + description: | + Include this for fixing functional issue with the TX termination + resistance in GT, which can be out of spec for the XCZU9EG silicon + version. + type: boolean + +required: + - "#phy-cells" + - compatible + - reg + - reg-names + +if: + properties: + compatible: + const: xlnx,zynqmp-psgtr-v1.1 + +then: + properties: + xlnx,tx-termination-fix: false + +additionalProperties: false + +examples: + - | + phy: phy@fd400000 { + compatible = "xlnx,zynqmp-psgtr-v1.1"; + reg = <0x0 0xfd400000 0x0 0x40000>, + <0x0 0xfd3d0000 0x0 0x1000>; + reg-names = "serdes", "siou"; + clocks = <&refclks 3>, <&refclks 2>, <&refclks 0>; + clock-names = "ref1", "ref2", "ref3"; + #phy-cells = <4>; + }; + +... diff --git a/include/dt-bindings/phy/phy.h b/include/dt-bindings/phy/phy.h index 3727ef72138b..36e8c241cf48 100644 --- a/include/dt-bindings/phy/phy.h +++ b/include/dt-bindings/phy/phy.h @@ -18,5 +18,6 @@ #define PHY_TYPE_UFS 5 #define PHY_TYPE_DP 6 #define PHY_TYPE_XPCS 7 +#define PHY_TYPE_SGMII 8 #endif /* _DT_BINDINGS_PHY */ -- cgit v1.2.3-59-g8ed1b From 4a33bea003144e217d8a3ae666f171dfc2e97bd6 Mon Sep 17 00:00:00 2001 From: Anurag Kumar Vulisha Date: Mon, 29 Jun 2020 15:00:53 +0300 Subject: phy: zynqmp: Add PHY driver for the Xilinx ZynqMP Gigabit Transceiver Xilinx ZynqMP SoCs have a Gigabit Transceiver with four lanes. All the high speed peripherals such as USB, SATA, PCIE, Display Port and Ethernet SGMII can rely on any of the four GT lanes for PHY layer. This patch adds driver for that ZynqMP GT core. Signed-off-by: Anurag Kumar Vulisha Signed-off-by: Laurent Pinchart Link: https://lore.kernel.org/r/20200629120054.29338-3-laurent.pinchart@ideasonboard.com Signed-off-by: Vinod Koul --- MAINTAINERS | 9 + drivers/phy/Kconfig | 1 + drivers/phy/Makefile | 3 +- drivers/phy/xilinx/Kconfig | 13 + drivers/phy/xilinx/Makefile | 3 + drivers/phy/xilinx/phy-zynqmp.c | 995 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 1023 insertions(+), 1 deletion(-) create mode 100644 drivers/phy/xilinx/Kconfig create mode 100644 drivers/phy/xilinx/Makefile create mode 100644 drivers/phy/xilinx/phy-zynqmp.c diff --git a/MAINTAINERS b/MAINTAINERS index 68f21d46614c..82a12abe24c6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -18852,6 +18852,15 @@ F: Documentation/devicetree/bindings/media/xilinx/ F: drivers/media/platform/xilinx/ F: include/uapi/linux/xilinx-v4l2-controls.h +XILINX ZYNQMP PSGTR PHY DRIVER +M: Anurag Kumar Vulisha +M: Laurent Pinchart +L: linux-kernel@vger.kernel.org +S: Supported +T: git https://github.com/Xilinx/linux-xlnx.git +F: Documentation/devicetree/bindings/phy/xlnx,zynqmp-psgtr.yaml +F: drivers/phy/xilinx/phy-zynqmp.c + XILLYBUS DRIVER M: Eli Billauer L: linux-kernel@vger.kernel.org diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index b3ed94b98d9b..de9362c25c07 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -70,5 +70,6 @@ source "drivers/phy/st/Kconfig" source "drivers/phy/tegra/Kconfig" source "drivers/phy/ti/Kconfig" source "drivers/phy/intel/Kconfig" +source "drivers/phy/xilinx/Kconfig" endmenu diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index 16e2622277d7..c27408e4daae 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -28,4 +28,5 @@ obj-y += allwinner/ \ socionext/ \ st/ \ tegra/ \ - ti/ + ti/ \ + xilinx/ diff --git a/drivers/phy/xilinx/Kconfig b/drivers/phy/xilinx/Kconfig new file mode 100644 index 000000000000..d8b0d46b2b4d --- /dev/null +++ b/drivers/phy/xilinx/Kconfig @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0-only + +# +# PHY drivers for Xilinx platforms +# + +config PHY_XILINX_ZYNQMP + tristate "Xilinx ZynqMP PHY driver" + depends on ARCH_ZYNQMP || COMPILE_TEST + select GENERIC_PHY + help + Enable this to support ZynqMP High Speed Gigabit Transceiver + that is part of ZynqMP SoC. diff --git a/drivers/phy/xilinx/Makefile b/drivers/phy/xilinx/Makefile new file mode 100644 index 000000000000..3f1f6a2a9b45 --- /dev/null +++ b/drivers/phy/xilinx/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_PHY_XILINX_ZYNQMP) += phy-zynqmp.o diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c new file mode 100644 index 000000000000..8babee2ce9ec --- /dev/null +++ b/drivers/phy/xilinx/phy-zynqmp.c @@ -0,0 +1,995 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * phy-zynqmp.c - PHY driver for Xilinx ZynqMP GT. + * + * Copyright (C) 2018-2020 Xilinx Inc. + * + * Author: Anurag Kumar Vulisha + * Author: Subbaraya Sundeep + * Author: Laurent Pinchart + * + * This driver is tested for USB, SATA and Display Port currently. + * Other controllers PCIe and SGMII should also work but that is + * experimental as of now. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* + * Lane Registers + */ + +/* TX De-emphasis parameters */ +#define L0_TX_ANA_TM_18 0x0048 +#define L0_TX_ANA_TM_118 0x01d8 +#define L0_TX_ANA_TM_118_FORCE_17_0 BIT(0) + +/* DN Resistor calibration code parameters */ +#define L0_TXPMA_ST_3 0x0b0c +#define L0_DN_CALIB_CODE 0x3f + +/* PMA control parameters */ +#define L0_TXPMD_TM_45 0x0cb4 +#define L0_TXPMD_TM_48 0x0cc0 +#define L0_TXPMD_TM_45_OVER_DP_MAIN BIT(0) +#define L0_TXPMD_TM_45_ENABLE_DP_MAIN BIT(1) +#define L0_TXPMD_TM_45_OVER_DP_POST1 BIT(2) +#define L0_TXPMD_TM_45_ENABLE_DP_POST1 BIT(3) +#define L0_TXPMD_TM_45_OVER_DP_POST2 BIT(4) +#define L0_TXPMD_TM_45_ENABLE_DP_POST2 BIT(5) + +/* PCS control parameters */ +#define L0_TM_DIG_6 0x106c +#define L0_TM_DIS_DESCRAMBLE_DECODER 0x0f +#define L0_TX_DIG_61 0x00f4 +#define L0_TM_DISABLE_SCRAMBLE_ENCODER 0x0f + +/* PLL Test Mode register parameters */ +#define L0_TM_PLL_DIG_37 0x2094 +#define L0_TM_COARSE_CODE_LIMIT 0x10 + +/* PLL SSC step size offsets */ +#define L0_PLL_SS_STEPS_0_LSB 0x2368 +#define L0_PLL_SS_STEPS_1_MSB 0x236c +#define L0_PLL_SS_STEP_SIZE_0_LSB 0x2370 +#define L0_PLL_SS_STEP_SIZE_1 0x2374 +#define L0_PLL_SS_STEP_SIZE_2 0x2378 +#define L0_PLL_SS_STEP_SIZE_3_MSB 0x237c +#define L0_PLL_STATUS_READ_1 0x23e4 + +/* SSC step size parameters */ +#define STEP_SIZE_0_MASK 0xff +#define STEP_SIZE_1_MASK 0xff +#define STEP_SIZE_2_MASK 0xff +#define STEP_SIZE_3_MASK 0x3 +#define STEP_SIZE_SHIFT 8 +#define FORCE_STEP_SIZE 0x10 +#define FORCE_STEPS 0x20 +#define STEPS_0_MASK 0xff +#define STEPS_1_MASK 0x07 + +/* Reference clock selection parameters */ +#define L0_Ln_REF_CLK_SEL(n) (0x2860 + (n) * 4) +#define L0_REF_CLK_SEL_MASK 0x8f + +/* Calibration digital logic parameters */ +#define L3_TM_CALIB_DIG19 0xec4c +#define L3_CALIB_DONE_STATUS 0xef14 +#define L3_TM_CALIB_DIG18 0xec48 +#define L3_TM_CALIB_DIG19_NSW 0x07 +#define L3_TM_CALIB_DIG18_NSW 0xe0 +#define L3_TM_OVERRIDE_NSW_CODE 0x20 +#define L3_CALIB_DONE 0x02 +#define L3_NSW_SHIFT 5 +#define L3_NSW_PIPE_SHIFT 4 +#define L3_NSW_CALIB_SHIFT 3 + +#define PHY_REG_OFFSET 0x4000 + +/* + * Global Registers + */ + +/* Refclk selection parameters */ +#define PLL_REF_SEL(n) (0x10000 + (n) * 4) +#define PLL_FREQ_MASK 0x1f +#define PLL_STATUS_LOCKED 0x10 + +/* Inter Connect Matrix parameters */ +#define ICM_CFG0 0x10010 +#define ICM_CFG1 0x10014 +#define ICM_CFG0_L0_MASK 0x07 +#define ICM_CFG0_L1_MASK 0x70 +#define ICM_CFG1_L2_MASK 0x07 +#define ICM_CFG2_L3_MASK 0x70 +#define ICM_CFG_SHIFT 4 + +/* Inter Connect Matrix allowed protocols */ +#define ICM_PROTOCOL_PD 0x0 +#define ICM_PROTOCOL_PCIE 0x1 +#define ICM_PROTOCOL_SATA 0x2 +#define ICM_PROTOCOL_USB 0x3 +#define ICM_PROTOCOL_DP 0x4 +#define ICM_PROTOCOL_SGMII 0x5 + +/* Test Mode common reset control parameters */ +#define TM_CMN_RST 0x10018 +#define TM_CMN_RST_EN 0x1 +#define TM_CMN_RST_SET 0x2 +#define TM_CMN_RST_MASK 0x3 + +/* Bus width parameters */ +#define TX_PROT_BUS_WIDTH 0x10040 +#define RX_PROT_BUS_WIDTH 0x10044 +#define PROT_BUS_WIDTH_10 0x0 +#define PROT_BUS_WIDTH_20 0x1 +#define PROT_BUS_WIDTH_40 0x2 +#define PROT_BUS_WIDTH_SHIFT 2 + +/* Number of GT lanes */ +#define NUM_LANES 4 + +/* SIOU SATA control register */ +#define SATA_CONTROL_OFFSET 0x0100 + +/* Total number of controllers */ +#define CONTROLLERS_PER_LANE 5 + +/* Protocol Type parameters */ +#define XPSGTR_TYPE_USB0 0 /* USB controller 0 */ +#define XPSGTR_TYPE_USB1 1 /* USB controller 1 */ +#define XPSGTR_TYPE_SATA_0 2 /* SATA controller lane 0 */ +#define XPSGTR_TYPE_SATA_1 3 /* SATA controller lane 1 */ +#define XPSGTR_TYPE_PCIE_0 4 /* PCIe controller lane 0 */ +#define XPSGTR_TYPE_PCIE_1 5 /* PCIe controller lane 1 */ +#define XPSGTR_TYPE_PCIE_2 6 /* PCIe controller lane 2 */ +#define XPSGTR_TYPE_PCIE_3 7 /* PCIe controller lane 3 */ +#define XPSGTR_TYPE_DP_0 8 /* Display Port controller lane 0 */ +#define XPSGTR_TYPE_DP_1 9 /* Display Port controller lane 1 */ +#define XPSGTR_TYPE_SGMII0 10 /* Ethernet SGMII controller 0 */ +#define XPSGTR_TYPE_SGMII1 11 /* Ethernet SGMII controller 1 */ +#define XPSGTR_TYPE_SGMII2 12 /* Ethernet SGMII controller 2 */ +#define XPSGTR_TYPE_SGMII3 13 /* Ethernet SGMII controller 3 */ + +/* Timeout values */ +#define TIMEOUT_US 1000 + +struct xpsgtr_dev; + +/** + * struct xpsgtr_ssc - structure to hold SSC settings for a lane + * @refclk_rate: PLL reference clock frequency + * @pll_ref_clk: value to be written to register for corresponding ref clk rate + * @steps: number of steps of SSC (Spread Spectrum Clock) + * @step_size: step size of each step + */ +struct xpsgtr_ssc { + u32 refclk_rate; + u8 pll_ref_clk; + u32 steps; + u32 step_size; +}; + +/** + * struct xpsgtr_phy - representation of a lane + * @phy: pointer to the kernel PHY device + * @type: controller which uses this lane + * @lane: lane number + * @protocol: protocol in which the lane operates + * @skip_phy_init: skip phy_init() if true + * @dev: pointer to the xpsgtr_dev instance + * @refclk: reference clock index + */ +struct xpsgtr_phy { + struct phy *phy; + u8 type; + u8 lane; + u8 protocol; + bool skip_phy_init; + struct xpsgtr_dev *dev; + unsigned int refclk; +}; + +/** + * struct xpsgtr_dev - representation of a ZynMP GT device + * @dev: pointer to device + * @serdes: serdes base address + * @siou: siou base address + * @gtr_mutex: mutex for locking + * @phys: PHY lanes + * @refclk_sscs: spread spectrum settings for the reference clocks + * @tx_term_fix: fix for GT issue + * @saved_icm_cfg0: stored value of ICM CFG0 register + * @saved_icm_cfg1: stored value of ICM CFG1 register + */ +struct xpsgtr_dev { + struct device *dev; + void __iomem *serdes; + void __iomem *siou; + struct mutex gtr_mutex; /* mutex for locking */ + struct xpsgtr_phy phys[NUM_LANES]; + const struct xpsgtr_ssc *refclk_sscs[NUM_LANES]; + bool tx_term_fix; + unsigned int saved_icm_cfg0; + unsigned int saved_icm_cfg1; +}; + +/* + * Configuration Data + */ + +/* lookup table to hold all settings needed for a ref clock frequency */ +static const struct xpsgtr_ssc ssc_lookup[] = { + { 19200000, 0x05, 608, 264020 }, + { 20000000, 0x06, 634, 243454 }, + { 24000000, 0x07, 760, 168973 }, + { 26000000, 0x08, 824, 143860 }, + { 27000000, 0x09, 856, 86551 }, + { 38400000, 0x0a, 1218, 65896 }, + { 40000000, 0x0b, 634, 243454 }, + { 52000000, 0x0c, 824, 143860 }, + { 100000000, 0x0d, 1058, 87533 }, + { 108000000, 0x0e, 856, 86551 }, + { 125000000, 0x0f, 992, 119497 }, + { 135000000, 0x10, 1070, 55393 }, + { 150000000, 0x11, 792, 187091 } +}; + +/* + * I/O Accessors + */ + +static inline u32 xpsgtr_read(struct xpsgtr_dev *gtr_dev, u32 reg) +{ + return readl(gtr_dev->serdes + reg); +} + +static inline void xpsgtr_write(struct xpsgtr_dev *gtr_dev, u32 reg, u32 value) +{ + writel(value, gtr_dev->serdes + reg); +} + +static inline void xpsgtr_clr_set(struct xpsgtr_dev *gtr_dev, u32 reg, + u32 clr, u32 set) +{ + u32 value = xpsgtr_read(gtr_dev, reg); + + value &= ~clr; + value |= set; + xpsgtr_write(gtr_dev, reg, value); +} + +static inline u32 xpsgtr_read_phy(struct xpsgtr_phy *gtr_phy, u32 reg) +{ + void __iomem *addr = gtr_phy->dev->serdes + + gtr_phy->lane * PHY_REG_OFFSET + reg; + + return readl(addr); +} + +static inline void xpsgtr_write_phy(struct xpsgtr_phy *gtr_phy, + u32 reg, u32 value) +{ + void __iomem *addr = gtr_phy->dev->serdes + + gtr_phy->lane * PHY_REG_OFFSET + reg; + + writel(value, addr); +} + +static inline void xpsgtr_clr_set_phy(struct xpsgtr_phy *gtr_phy, + u32 reg, u32 clr, u32 set) +{ + void __iomem *addr = gtr_phy->dev->serdes + + gtr_phy->lane * PHY_REG_OFFSET + reg; + + writel((readl(addr) & ~clr) | set, addr); +} + +/* + * Hardware Configuration + */ + +/* Wait for the PLL to lock (with a timeout). */ +static int xpsgtr_wait_pll_lock(struct phy *phy) +{ + struct xpsgtr_phy *gtr_phy = phy_get_drvdata(phy); + struct xpsgtr_dev *gtr_dev = gtr_phy->dev; + unsigned int timeout = TIMEOUT_US; + int ret; + + dev_dbg(gtr_dev->dev, "Waiting for PLL lock\n"); + + while (1) { + u32 reg = xpsgtr_read_phy(gtr_phy, L0_PLL_STATUS_READ_1); + + if ((reg & PLL_STATUS_LOCKED) == PLL_STATUS_LOCKED) { + ret = 0; + break; + } + + if (--timeout == 0) { + ret = -ETIMEDOUT; + break; + } + + udelay(1); + } + + if (ret == -ETIMEDOUT) + dev_err(gtr_dev->dev, + "lane %u (type %u, protocol %u): PLL lock timeout\n", + gtr_phy->lane, gtr_phy->type, gtr_phy->protocol); + + return ret; +} + +/* Configure PLL and spread-sprectrum clock. */ +static void xpsgtr_configure_pll(struct xpsgtr_phy *gtr_phy) +{ + const struct xpsgtr_ssc *ssc; + u32 step_size; + + ssc = gtr_phy->dev->refclk_sscs[gtr_phy->refclk]; + step_size = ssc->step_size; + + xpsgtr_clr_set(gtr_phy->dev, PLL_REF_SEL(gtr_phy->lane), + PLL_FREQ_MASK, ssc->pll_ref_clk); + + /* Enable lane clock sharing, if required */ + if (gtr_phy->refclk != gtr_phy->lane) { + /* Lane3 Ref Clock Selection Register */ + xpsgtr_clr_set(gtr_phy->dev, L0_Ln_REF_CLK_SEL(gtr_phy->lane), + L0_REF_CLK_SEL_MASK, 1 << gtr_phy->refclk); + } + + /* SSC step size [7:0] */ + xpsgtr_clr_set_phy(gtr_phy, L0_PLL_SS_STEP_SIZE_0_LSB, + STEP_SIZE_0_MASK, step_size & STEP_SIZE_0_MASK); + + /* SSC step size [15:8] */ + step_size >>= STEP_SIZE_SHIFT; + xpsgtr_clr_set_phy(gtr_phy, L0_PLL_SS_STEP_SIZE_1, + STEP_SIZE_1_MASK, step_size & STEP_SIZE_1_MASK); + + /* SSC step size [23:16] */ + step_size >>= STEP_SIZE_SHIFT; + xpsgtr_clr_set_phy(gtr_phy, L0_PLL_SS_STEP_SIZE_2, + STEP_SIZE_2_MASK, step_size & STEP_SIZE_2_MASK); + + /* SSC steps [7:0] */ + xpsgtr_clr_set_phy(gtr_phy, L0_PLL_SS_STEPS_0_LSB, + STEPS_0_MASK, ssc->steps & STEPS_0_MASK); + + /* SSC steps [10:8] */ + xpsgtr_clr_set_phy(gtr_phy, L0_PLL_SS_STEPS_1_MSB, + STEPS_1_MASK, + (ssc->steps >> STEP_SIZE_SHIFT) & STEPS_1_MASK); + + /* SSC step size [24:25] */ + step_size >>= STEP_SIZE_SHIFT; + xpsgtr_clr_set_phy(gtr_phy, L0_PLL_SS_STEP_SIZE_3_MSB, + STEP_SIZE_3_MASK, (step_size & STEP_SIZE_3_MASK) | + FORCE_STEP_SIZE | FORCE_STEPS); +} + +/* Configure the lane protocol. */ +static void xpsgtr_lane_set_protocol(struct xpsgtr_phy *gtr_phy) +{ + struct xpsgtr_dev *gtr_dev = gtr_phy->dev; + u8 protocol = gtr_phy->protocol; + + switch (gtr_phy->lane) { + case 0: + xpsgtr_clr_set(gtr_dev, ICM_CFG0, ICM_CFG0_L0_MASK, protocol); + break; + case 1: + xpsgtr_clr_set(gtr_dev, ICM_CFG0, ICM_CFG0_L1_MASK, + protocol << ICM_CFG_SHIFT); + break; + case 2: + xpsgtr_clr_set(gtr_dev, ICM_CFG1, ICM_CFG0_L0_MASK, protocol); + break; + case 3: + xpsgtr_clr_set(gtr_dev, ICM_CFG1, ICM_CFG0_L1_MASK, + protocol << ICM_CFG_SHIFT); + break; + default: + /* We already checked 0 <= lane <= 3 */ + break; + } +} + +/* Bypass (de)scrambler and 8b/10b decoder and encoder. */ +static void xpsgtr_bypass_scrambler_8b10b(struct xpsgtr_phy *gtr_phy) +{ + xpsgtr_write_phy(gtr_phy, L0_TM_DIG_6, L0_TM_DIS_DESCRAMBLE_DECODER); + xpsgtr_write_phy(gtr_phy, L0_TX_DIG_61, L0_TM_DISABLE_SCRAMBLE_ENCODER); +} + +/* DP-specific initialization. */ +static void xpsgtr_phy_init_dp(struct xpsgtr_phy *gtr_phy) +{ + xpsgtr_write_phy(gtr_phy, L0_TXPMD_TM_45, + L0_TXPMD_TM_45_OVER_DP_MAIN | + L0_TXPMD_TM_45_ENABLE_DP_MAIN | + L0_TXPMD_TM_45_OVER_DP_POST1 | + L0_TXPMD_TM_45_OVER_DP_POST2 | + L0_TXPMD_TM_45_ENABLE_DP_POST2); + xpsgtr_write_phy(gtr_phy, L0_TX_ANA_TM_118, + L0_TX_ANA_TM_118_FORCE_17_0); +} + +/* SATA-specific initialization. */ +static void xpsgtr_phy_init_sata(struct xpsgtr_phy *gtr_phy) +{ + struct xpsgtr_dev *gtr_dev = gtr_phy->dev; + + xpsgtr_bypass_scrambler_8b10b(gtr_phy); + + writel(gtr_phy->lane, gtr_dev->siou + SATA_CONTROL_OFFSET); +} + +/* SGMII-specific initialization. */ +static void xpsgtr_phy_init_sgmii(struct xpsgtr_phy *gtr_phy) +{ + struct xpsgtr_dev *gtr_dev = gtr_phy->dev; + + /* Set SGMII protocol TX and RX bus width to 10 bits. */ + xpsgtr_write(gtr_dev, TX_PROT_BUS_WIDTH, + PROT_BUS_WIDTH_10 << (gtr_phy->lane * PROT_BUS_WIDTH_SHIFT)); + xpsgtr_write(gtr_dev, RX_PROT_BUS_WIDTH, + PROT_BUS_WIDTH_10 << (gtr_phy->lane * PROT_BUS_WIDTH_SHIFT)); + + xpsgtr_bypass_scrambler_8b10b(gtr_phy); +} + +/* Configure TX de-emphasis and margining for DP. */ +static void xpsgtr_phy_configure_dp(struct xpsgtr_phy *gtr_phy, unsigned int pre, + unsigned int voltage) +{ + static const u8 voltage_swing[4][4] = { + { 0x2a, 0x27, 0x24, 0x20 }, + { 0x27, 0x23, 0x20, 0xff }, + { 0x24, 0x20, 0xff, 0xff }, + { 0xff, 0xff, 0xff, 0xff } + }; + static const u8 pre_emphasis[4][4] = { + { 0x02, 0x02, 0x02, 0x02 }, + { 0x01, 0x01, 0x01, 0xff }, + { 0x00, 0x00, 0xff, 0xff }, + { 0xff, 0xff, 0xff, 0xff } + }; + + xpsgtr_write_phy(gtr_phy, L0_TXPMD_TM_48, voltage_swing[pre][voltage]); + xpsgtr_write_phy(gtr_phy, L0_TX_ANA_TM_18, pre_emphasis[pre][voltage]); +} + +/* + * PHY Operations + */ + +static bool xpsgtr_phy_init_required(struct xpsgtr_phy *gtr_phy) +{ + /* + * As USB may save the snapshot of the states during hibernation, doing + * phy_init() will put the USB controller into reset, resulting in the + * losing of the saved snapshot. So try to avoid phy_init() for USB + * except when gtr_phy->skip_phy_init is false (this happens when FPD is + * shutdown during suspend or when gt lane is changed from current one) + */ + if (gtr_phy->protocol == ICM_PROTOCOL_USB && gtr_phy->skip_phy_init) + return false; + else + return true; +} + +/* + * There is a functional issue in the GT. The TX termination resistance can be + * out of spec due to a issue in the calibration logic. This is the workaround + * to fix it, required for XCZU9EG silicon. + */ +static int xpsgtr_phy_tx_term_fix(struct xpsgtr_phy *gtr_phy) +{ + struct xpsgtr_dev *gtr_dev = gtr_phy->dev; + u32 timeout = TIMEOUT_US; + u32 nsw; + + /* Enabling Test Mode control for CMN Rest */ + xpsgtr_clr_set(gtr_dev, TM_CMN_RST, TM_CMN_RST_MASK, TM_CMN_RST_SET); + + /* Set Test Mode reset */ + xpsgtr_clr_set(gtr_dev, TM_CMN_RST, TM_CMN_RST_MASK, TM_CMN_RST_EN); + + xpsgtr_write(gtr_dev, L3_TM_CALIB_DIG18, 0x00); + xpsgtr_write(gtr_dev, L3_TM_CALIB_DIG19, L3_TM_OVERRIDE_NSW_CODE); + + /* + * As a part of work around sequence for PMOS calibration fix, + * we need to configure any lane ICM_CFG to valid protocol. This + * will deassert the CMN_Resetn signal. + */ + xpsgtr_lane_set_protocol(gtr_phy); + + /* Clear Test Mode reset */ + xpsgtr_clr_set(gtr_dev, TM_CMN_RST, TM_CMN_RST_MASK, TM_CMN_RST_SET); + + dev_dbg(gtr_dev->dev, "calibrating...\n"); + + do { + u32 reg = xpsgtr_read(gtr_dev, L3_CALIB_DONE_STATUS); + + if ((reg & L3_CALIB_DONE) == L3_CALIB_DONE) + break; + + if (!--timeout) { + dev_err(gtr_dev->dev, "calibration time out\n"); + return -ETIMEDOUT; + } + + udelay(1); + } while (timeout > 0); + + dev_dbg(gtr_dev->dev, "calibration done\n"); + + /* Reading NMOS Register Code */ + nsw = xpsgtr_read(gtr_dev, L0_TXPMA_ST_3) & L0_DN_CALIB_CODE; + + /* Set Test Mode reset */ + xpsgtr_clr_set(gtr_dev, TM_CMN_RST, TM_CMN_RST_MASK, TM_CMN_RST_EN); + + /* Writing NMOS register values back [5:3] */ + xpsgtr_write(gtr_dev, L3_TM_CALIB_DIG19, nsw >> L3_NSW_CALIB_SHIFT); + + /* Writing NMOS register value [2:0] */ + xpsgtr_write(gtr_dev, L3_TM_CALIB_DIG18, + ((nsw & L3_TM_CALIB_DIG19_NSW) << L3_NSW_SHIFT) | + (1 << L3_NSW_PIPE_SHIFT)); + + /* Clear Test Mode reset */ + xpsgtr_clr_set(gtr_dev, TM_CMN_RST, TM_CMN_RST_MASK, TM_CMN_RST_SET); + + return 0; +} + +static int xpsgtr_phy_init(struct phy *phy) +{ + struct xpsgtr_phy *gtr_phy = phy_get_drvdata(phy); + struct xpsgtr_dev *gtr_dev = gtr_phy->dev; + int ret = 0; + + mutex_lock(>r_dev->gtr_mutex); + + /* Skip initialization if not required. */ + if (!xpsgtr_phy_init_required(gtr_phy)) + goto out; + + if (gtr_dev->tx_term_fix) { + ret = xpsgtr_phy_tx_term_fix(gtr_phy); + if (ret < 0) + goto out; + + gtr_dev->tx_term_fix = false; + } + + /* Enable coarse code saturation limiting logic. */ + xpsgtr_write_phy(gtr_phy, L0_TM_PLL_DIG_37, L0_TM_COARSE_CODE_LIMIT); + + /* + * Configure the PLL, the lane protocol, and perform protocol-specific + * initialization. + */ + xpsgtr_configure_pll(gtr_phy); + xpsgtr_lane_set_protocol(gtr_phy); + + switch (gtr_phy->protocol) { + case ICM_PROTOCOL_DP: + xpsgtr_phy_init_dp(gtr_phy); + break; + + case ICM_PROTOCOL_SATA: + xpsgtr_phy_init_sata(gtr_phy); + break; + + case ICM_PROTOCOL_SGMII: + xpsgtr_phy_init_sgmii(gtr_phy); + break; + } + +out: + mutex_unlock(>r_dev->gtr_mutex); + return ret; +} + +static int xpsgtr_phy_exit(struct phy *phy) +{ + struct xpsgtr_phy *gtr_phy = phy_get_drvdata(phy); + + gtr_phy->skip_phy_init = false; + + return 0; +} + +static int xpsgtr_phy_power_on(struct phy *phy) +{ + struct xpsgtr_phy *gtr_phy = phy_get_drvdata(phy); + int ret = 0; + + /* + * Wait for the PLL to lock. For DP, only wait on DP0 to avoid + * cumulating waits for both lanes. The user is expected to initialize + * lane 0 last. + */ + if (gtr_phy->protocol != ICM_PROTOCOL_DP || + gtr_phy->type == XPSGTR_TYPE_DP_0) + ret = xpsgtr_wait_pll_lock(phy); + + return ret; +} + +static int xpsgtr_phy_configure(struct phy *phy, union phy_configure_opts *opts) +{ + struct xpsgtr_phy *gtr_phy = phy_get_drvdata(phy); + + if (gtr_phy->protocol != ICM_PROTOCOL_DP) + return 0; + + xpsgtr_phy_configure_dp(gtr_phy, opts->dp.pre[0], opts->dp.voltage[0]); + + return 0; +} + +static const struct phy_ops xpsgtr_phyops = { + .init = xpsgtr_phy_init, + .exit = xpsgtr_phy_exit, + .power_on = xpsgtr_phy_power_on, + .configure = xpsgtr_phy_configure, + .owner = THIS_MODULE, +}; + +/* + * OF Xlate Support + */ + +/* Set the lane type and protocol based on the PHY type and instance number. */ +static int xpsgtr_set_lane_type(struct xpsgtr_phy *gtr_phy, u8 phy_type, + unsigned int phy_instance) +{ + unsigned int num_phy_types; + const int *phy_types; + + switch (phy_type) { + case PHY_TYPE_SATA: { + static const int types[] = { + XPSGTR_TYPE_SATA_0, + XPSGTR_TYPE_SATA_1, + }; + + phy_types = types; + num_phy_types = ARRAY_SIZE(types); + gtr_phy->protocol = ICM_PROTOCOL_SATA; + break; + } + case PHY_TYPE_USB3: { + static const int types[] = { + XPSGTR_TYPE_USB0, + XPSGTR_TYPE_USB1, + }; + + phy_types = types; + num_phy_types = ARRAY_SIZE(types); + gtr_phy->protocol = ICM_PROTOCOL_USB; + break; + } + case PHY_TYPE_DP: { + static const int types[] = { + XPSGTR_TYPE_DP_0, + XPSGTR_TYPE_DP_1, + }; + + phy_types = types; + num_phy_types = ARRAY_SIZE(types); + gtr_phy->protocol = ICM_PROTOCOL_DP; + break; + } + case PHY_TYPE_PCIE: { + static const int types[] = { + XPSGTR_TYPE_PCIE_0, + XPSGTR_TYPE_PCIE_1, + XPSGTR_TYPE_PCIE_2, + XPSGTR_TYPE_PCIE_3, + }; + + phy_types = types; + num_phy_types = ARRAY_SIZE(types); + gtr_phy->protocol = ICM_PROTOCOL_PCIE; + break; + } + case PHY_TYPE_SGMII: { + static const int types[] = { + XPSGTR_TYPE_SGMII0, + XPSGTR_TYPE_SGMII1, + XPSGTR_TYPE_SGMII2, + XPSGTR_TYPE_SGMII3, + }; + + phy_types = types; + num_phy_types = ARRAY_SIZE(types); + gtr_phy->protocol = ICM_PROTOCOL_SGMII; + break; + } + default: + return -EINVAL; + } + + if (phy_instance >= num_phy_types) + return -EINVAL; + + gtr_phy->type = phy_types[phy_instance]; + return 0; +} + +/* + * Valid combinations of controllers and lanes (Interconnect Matrix). + */ +static const unsigned int icm_matrix[NUM_LANES][CONTROLLERS_PER_LANE] = { + { XPSGTR_TYPE_PCIE_0, XPSGTR_TYPE_SATA_0, XPSGTR_TYPE_USB0, + XPSGTR_TYPE_DP_1, XPSGTR_TYPE_SGMII0 }, + { XPSGTR_TYPE_PCIE_1, XPSGTR_TYPE_SATA_1, XPSGTR_TYPE_USB0, + XPSGTR_TYPE_DP_0, XPSGTR_TYPE_SGMII1 }, + { XPSGTR_TYPE_PCIE_2, XPSGTR_TYPE_SATA_0, XPSGTR_TYPE_USB0, + XPSGTR_TYPE_DP_1, XPSGTR_TYPE_SGMII2 }, + { XPSGTR_TYPE_PCIE_3, XPSGTR_TYPE_SATA_1, XPSGTR_TYPE_USB1, + XPSGTR_TYPE_DP_0, XPSGTR_TYPE_SGMII3 } +}; + +/* Translate OF phandle and args to PHY instance. */ +static struct phy *xpsgtr_xlate(struct device *dev, + struct of_phandle_args *args) +{ + struct xpsgtr_dev *gtr_dev = dev_get_drvdata(dev); + struct xpsgtr_phy *gtr_phy; + unsigned int phy_instance; + unsigned int phy_lane; + unsigned int phy_type; + unsigned int refclk; + unsigned int i; + int ret; + + if (args->args_count != 4) { + dev_err(dev, "Invalid number of cells in 'phy' property\n"); + return ERR_PTR(-EINVAL); + } + + /* + * Get the PHY parameters from the OF arguments and derive the lane + * type. + */ + phy_lane = args->args[0]; + if (phy_lane >= ARRAY_SIZE(gtr_dev->phys)) { + dev_err(dev, "Invalid lane number %u\n", phy_lane); + return ERR_PTR(-ENODEV); + } + + gtr_phy = >r_dev->phys[phy_lane]; + phy_type = args->args[1]; + phy_instance = args->args[2]; + + ret = xpsgtr_set_lane_type(gtr_phy, phy_type, phy_instance); + if (ret < 0) { + dev_err(gtr_dev->dev, "Invalid PHY type and/or instance\n"); + return ERR_PTR(ret); + } + + refclk = args->args[3]; + if (refclk >= ARRAY_SIZE(gtr_dev->refclk_sscs) || + !gtr_dev->refclk_sscs[refclk]) { + dev_err(dev, "Invalid reference clock number %u\n", refclk); + return ERR_PTR(-EINVAL); + } + + gtr_phy->refclk = refclk; + + /* + * Ensure that the Interconnect Matrix is obeyed, i.e a given lane type + * is allowed to operate on the lane. + */ + for (i = 0; i < CONTROLLERS_PER_LANE; i++) { + if (icm_matrix[phy_lane][i] == gtr_phy->type) + return gtr_phy->phy; + } + + return ERR_PTR(-EINVAL); +} + +/* + * Power Management + */ + +#ifdef CONFIG_PM +static int xpsgtr_suspend(struct device *dev) +{ + struct xpsgtr_dev *gtr_dev = dev_get_drvdata(dev); + + /* Save the snapshot ICM_CFG registers. */ + gtr_dev->saved_icm_cfg0 = xpsgtr_read(gtr_dev, ICM_CFG0); + gtr_dev->saved_icm_cfg1 = xpsgtr_read(gtr_dev, ICM_CFG1); + + return 0; +} + +static int xpsgtr_resume(struct device *dev) +{ + struct xpsgtr_dev *gtr_dev = dev_get_drvdata(dev); + unsigned int icm_cfg0, icm_cfg1; + unsigned int i; + bool skip_phy_init; + + icm_cfg0 = xpsgtr_read(gtr_dev, ICM_CFG0); + icm_cfg1 = xpsgtr_read(gtr_dev, ICM_CFG1); + + /* Return if no GT lanes got configured before suspend. */ + if (!gtr_dev->saved_icm_cfg0 && !gtr_dev->saved_icm_cfg1) + return 0; + + /* Check if the ICM configurations changed after suspend. */ + if (icm_cfg0 == gtr_dev->saved_icm_cfg0 && + icm_cfg1 == gtr_dev->saved_icm_cfg1) + skip_phy_init = true; + else + skip_phy_init = false; + + /* Update the skip_phy_init for all gtr_phy instances. */ + for (i = 0; i < ARRAY_SIZE(gtr_dev->phys); i++) + gtr_dev->phys[i].skip_phy_init = skip_phy_init; + + return 0; +} +#endif /* CONFIG_PM */ + +static const struct dev_pm_ops xpsgtr_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(xpsgtr_suspend, xpsgtr_resume) +}; + +/* + * Probe & Platform Driver + */ + +static int xpsgtr_get_ref_clocks(struct xpsgtr_dev *gtr_dev) +{ + unsigned int refclk; + + for (refclk = 0; refclk < ARRAY_SIZE(gtr_dev->refclk_sscs); ++refclk) { + unsigned long rate; + unsigned int i; + struct clk *clk; + char name[8]; + + snprintf(name, sizeof(name), "ref%u", refclk); + clk = devm_clk_get_optional(gtr_dev->dev, name); + if (IS_ERR(clk)) { + if (PTR_ERR(clk) != -EPROBE_DEFER) + dev_err(gtr_dev->dev, + "Failed to get reference clock %u: %ld\n", + refclk, PTR_ERR(clk)); + return PTR_ERR(clk); + } + + if (!clk) + continue; + + /* + * Get the spread spectrum (SSC) settings for the reference + * clock rate. + */ + rate = clk_get_rate(clk); + + for (i = 0 ; i < ARRAY_SIZE(ssc_lookup); i++) { + if (rate == ssc_lookup[i].refclk_rate) { + gtr_dev->refclk_sscs[refclk] = &ssc_lookup[i]; + break; + } + } + + if (i == ARRAY_SIZE(ssc_lookup)) { + dev_err(gtr_dev->dev, + "Invalid rate %lu for reference clock %u\n", + rate, refclk); + return -EINVAL; + } + } + + return 0; +} + +static int xpsgtr_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct xpsgtr_dev *gtr_dev; + struct phy_provider *provider; + unsigned int port; + int ret; + + gtr_dev = devm_kzalloc(&pdev->dev, sizeof(*gtr_dev), GFP_KERNEL); + if (!gtr_dev) + return -ENOMEM; + + gtr_dev->dev = &pdev->dev; + platform_set_drvdata(pdev, gtr_dev); + + mutex_init(>r_dev->gtr_mutex); + + if (of_device_is_compatible(np, "xlnx,zynqmp-psgtr")) + gtr_dev->tx_term_fix = + of_property_read_bool(np, "xlnx,tx-termination-fix"); + + /* Acquire resources. */ + gtr_dev->serdes = devm_platform_ioremap_resource_byname(pdev, "serdes"); + if (IS_ERR(gtr_dev->serdes)) + return PTR_ERR(gtr_dev->serdes); + + gtr_dev->siou = devm_platform_ioremap_resource_byname(pdev, "siou"); + if (IS_ERR(gtr_dev->siou)) + return PTR_ERR(gtr_dev->siou); + + ret = xpsgtr_get_ref_clocks(gtr_dev); + if (ret) + return ret; + + /* Create PHYs. */ + for (port = 0; port < ARRAY_SIZE(gtr_dev->phys); ++port) { + struct xpsgtr_phy *gtr_phy = >r_dev->phys[port]; + struct phy *phy; + + gtr_phy->lane = port; + gtr_phy->dev = gtr_dev; + + phy = devm_phy_create(&pdev->dev, np, &xpsgtr_phyops); + if (IS_ERR(phy)) { + dev_err(&pdev->dev, "failed to create PHY\n"); + return PTR_ERR(phy); + } + + gtr_phy->phy = phy; + phy_set_drvdata(phy, gtr_phy); + } + + /* Register the PHY provider. */ + provider = devm_of_phy_provider_register(&pdev->dev, xpsgtr_xlate); + if (IS_ERR(provider)) { + dev_err(&pdev->dev, "registering provider failed\n"); + return PTR_ERR(provider); + } + return 0; +} + +static const struct of_device_id xpsgtr_of_match[] = { + { .compatible = "xlnx,zynqmp-psgtr", }, + { .compatible = "xlnx,zynqmp-psgtr-v1.1", }, + {}, +}; +MODULE_DEVICE_TABLE(of, xpsgtr_of_match); + +static struct platform_driver xpsgtr_driver = { + .probe = xpsgtr_probe, + .driver = { + .name = "xilinx-psgtr", + .of_match_table = xpsgtr_of_match, + .pm = &xpsgtr_pm_ops, + }, +}; + +module_platform_driver(xpsgtr_driver); + +MODULE_AUTHOR("Xilinx Inc."); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Xilinx ZynqMP High speed Gigabit Transceiver"); -- cgit v1.2.3-59-g8ed1b From 85c5cbeba8f4fb28e6b9bfb3e467718385f78f76 Mon Sep 17 00:00:00 2001 From: Wang Hai Date: Tue, 2 Jun 2020 20:07:33 +0800 Subject: cxl: Fix kobject memleak Currently the error return path from kobject_init_and_add() is not followed by a call to kobject_put() - which means we are leaking the kobject. Fix it by adding a call to kobject_put() in the error path of kobject_init_and_add(). Fixes: b087e6190ddc ("cxl: Export optional AFU configuration record in sysfs") Reported-by: Hulk Robot Signed-off-by: Wang Hai Acked-by: Andrew Donnellan Acked-by: Frederic Barrat Link: https://lore.kernel.org/r/20200602120733.5943-1-wanghai38@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/cxl/sysfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/cxl/sysfs.c b/drivers/misc/cxl/sysfs.c index f0263d1a1fdf..d97a243ad30c 100644 --- a/drivers/misc/cxl/sysfs.c +++ b/drivers/misc/cxl/sysfs.c @@ -624,7 +624,7 @@ static struct afu_config_record *cxl_sysfs_afu_new_cr(struct cxl_afu *afu, int c rc = kobject_init_and_add(&cr->kobj, &afu_config_record_type, &afu->dev.kobj, "cr%i", cr->cr); if (rc) - goto err; + goto err1; rc = sysfs_create_bin_file(&cr->kobj, &cr->config_attr); if (rc) -- cgit v1.2.3-59-g8ed1b From b7820ae2d2929d4d6e52462a820e760db35fdad6 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 29 May 2020 16:57:26 +0200 Subject: mux: adgs1408: Add mod_devicetable.h and remove of_match_ptr Enables probing via the ACPI PRP0001 route but more is mostly about removing examples of this that might get copied into new drivers. Also fixes drivers/mux/adgs1408.c:112:34: warning: unused variable 'adgs1408_of_match as has been reported recently. Fixes: e9e40543ad5b ("spi: Add generic SPI multiplexer") Reported-by: kbuild test robot Signed-off-by: Andy Shevchenko Signed-off-by: Peter Rosin Link: https://lore.kernel.org/r/20200529145726.5708-1-peda@axentia.se Signed-off-by: Greg Kroah-Hartman --- drivers/mux/adgs1408.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/mux/adgs1408.c b/drivers/mux/adgs1408.c index 89096f10f4c4..12466b06692c 100644 --- a/drivers/mux/adgs1408.c +++ b/drivers/mux/adgs1408.c @@ -6,9 +6,9 @@ */ #include +#include #include #include -#include #include #include @@ -59,7 +59,7 @@ static int adgs1408_probe(struct spi_device *spi) s32 idle_state; int ret; - chip_id = (enum adgs1408_chip_id)of_device_get_match_data(dev); + chip_id = (enum adgs1408_chip_id)device_get_match_data(dev); if (!chip_id) chip_id = spi_get_device_id(spi)->driver_data; @@ -119,7 +119,7 @@ MODULE_DEVICE_TABLE(of, adgs1408_of_match); static struct spi_driver adgs1408_driver = { .driver = { .name = "adgs1408", - .of_match_table = of_match_ptr(adgs1408_of_match), + .of_match_table = adgs1408_of_match, }, .probe = adgs1408_probe, .id_table = adgs1408_spi_id, -- cgit v1.2.3-59-g8ed1b From 68daf1dffad28f1d12e76358920d7d09d4371b52 Mon Sep 17 00:00:00 2001 From: Liao Pingfang Date: Sat, 30 May 2020 15:34:01 +0800 Subject: misc: mic: Remove the error message as the call will print it The message should just be dropped as the call will print the failure message anyway. Signed-off-by: Liao Pingfang Link: https://lore.kernel.org/r/1590824041-36500-1-git-send-email-wang.yi59@zte.com.cn Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/host/mic_main.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/misc/mic/host/mic_main.c b/drivers/misc/mic/host/mic_main.c index be0784fd1635..ea4608527ea0 100644 --- a/drivers/misc/mic/host/mic_main.c +++ b/drivers/misc/mic/host/mic_main.c @@ -164,7 +164,6 @@ static int mic_probe(struct pci_dev *pdev, mdev = kzalloc(sizeof(*mdev), GFP_KERNEL); if (!mdev) { rc = -ENOMEM; - dev_err(&pdev->dev, "mdev kmalloc failed rc %d\n", rc); goto mdev_alloc_fail; } mdev->id = ida_simple_get(&g_mic_ida, 0, MIC_MAX_NUM_DEVS, GFP_KERNEL); -- cgit v1.2.3-59-g8ed1b From 8374680ff2eb01a58314562c895b119c75c53c2a Mon Sep 17 00:00:00 2001 From: Rikard Falkeborn Date: Thu, 11 Jun 2020 00:47:04 +0200 Subject: mic: vop: Constify static structs vop_vq_config_ops and id_table[] are never modified so make them const to allow the compiler to put them in read-only memory. Before: text data bss dec hex filename 14889 4065 192 19146 4aca drivers/misc/mic/vop/vop_main.o After: text data bss dec hex filename 15113 3841 192 19146 4aca drivers/misc/mic/vop/vop_main.o Signed-off-by: Rikard Falkeborn Link: https://lore.kernel.org/r/20200610224704.27082-5-rikard.falkeborn@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/vop/vop_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/misc/mic/vop/vop_main.c b/drivers/misc/mic/vop/vop_main.c index 85942f6717c5..51a1ab83a630 100644 --- a/drivers/misc/mic/vop/vop_main.c +++ b/drivers/misc/mic/vop/vop_main.c @@ -438,7 +438,7 @@ error: /* * The config ops structure as defined by virtio config */ -static struct virtio_config_ops vop_vq_config_ops = { +static const struct virtio_config_ops vop_vq_config_ops = { .get_features = vop_get_features, .finalize_features = vop_finalize_features, .get = vop_get, @@ -763,7 +763,7 @@ static void vop_driver_remove(struct vop_device *vpdev) kfree(vi); } -static struct vop_device_id id_table[] = { +static const struct vop_device_id id_table[] = { { VOP_DEV_TRNSP, VOP_DEV_ANY_ID }, { 0 }, }; -- cgit v1.2.3-59-g8ed1b From 50bf73bdce8efc57f563297a6c2fedaedefa849e Mon Sep 17 00:00:00 2001 From: Rikard Falkeborn Date: Thu, 11 Jun 2020 00:47:03 +0200 Subject: mei: hdcp: Constify struct mei_cl_device_id mei_hdcp_tbl[] is never modified and can be made const to allow the compiler to put it in read-only memory. Before: text data bss dec hex filename 15844 5416 0 21260 530c drivers/misc/mei/hdcp/mei_hdcp.o After: text data bss dec hex filename 16004 5256 0 21260 530c drivers/misc/mei/hdcp/mei_hdcp.o Signed-off-by: Rikard Falkeborn Link: https://lore.kernel.org/r/20200610224704.27082-4-rikard.falkeborn@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/hdcp/mei_hdcp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/mei/hdcp/mei_hdcp.c b/drivers/misc/mei/hdcp/mei_hdcp.c index 4c596c646ac0..e6c3dc595617 100644 --- a/drivers/misc/mei/hdcp/mei_hdcp.c +++ b/drivers/misc/mei/hdcp/mei_hdcp.c @@ -852,7 +852,7 @@ static int mei_hdcp_remove(struct mei_cl_device *cldev) #define MEI_UUID_HDCP GUID_INIT(0xB638AB7E, 0x94E2, 0x4EA2, 0xA5, \ 0x52, 0xD1, 0xC5, 0x4B, 0x62, 0x7F, 0x04) -static struct mei_cl_device_id mei_hdcp_tbl[] = { +static const struct mei_cl_device_id mei_hdcp_tbl[] = { { .uuid = MEI_UUID_HDCP, .version = MEI_CL_VERSION_ANY }, { } }; -- cgit v1.2.3-59-g8ed1b From f08387903d949b760ed2abdde85fe6f45961a392 Mon Sep 17 00:00:00 2001 From: Rikard Falkeborn Date: Thu, 11 Jun 2020 00:47:02 +0200 Subject: misc: genwqe: Constify struct pci_error_handlers genwqe_err_handler is never modified, so it can be made const to allow the compiler to put it in read-only memory. Before: text data bss dec hex filename 20174 6104 2464 28742 7046 drivers/misc/genwqe/card_base.o After: text data bss dec hex filename 20270 6008 2464 28742 7046 drivers/misc/genwqe/card_base.o Signed-off-by: Rikard Falkeborn Link: https://lore.kernel.org/r/20200610224704.27082-3-rikard.falkeborn@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/genwqe/card_base.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/genwqe/card_base.c b/drivers/misc/genwqe/card_base.c index 1dc6c7c5cbce..9969c0003f15 100644 --- a/drivers/misc/genwqe/card_base.c +++ b/drivers/misc/genwqe/card_base.c @@ -1324,7 +1324,7 @@ static int genwqe_sriov_configure(struct pci_dev *dev, int numvfs) return 0; } -static struct pci_error_handlers genwqe_err_handler = { +static const struct pci_error_handlers genwqe_err_handler = { .error_detected = genwqe_err_error_detected, .mmio_enabled = genwqe_err_result_none, .slot_reset = genwqe_err_slot_reset, -- cgit v1.2.3-59-g8ed1b From 0e3a52ba93ffce737c7a2418ce6592152787d148 Mon Sep 17 00:00:00 2001 From: Rikard Falkeborn Date: Thu, 11 Jun 2020 00:47:01 +0200 Subject: misc: rtsx_usb: Constify struct usb_device_id rtsx_usb_usb_ids is never modified and can therefore be made const to allow the compiler to put it in read-only memory. Before: text data bss dec hex filename 21513 4160 128 25801 64c9 drivers/misc/cardreader/rtsx_usb.o After: text data bss dec hex filename 21673 4000 128 25801 64c9 drivers/misc/cardreader/rtsx_usb.o Signed-off-by: Rikard Falkeborn Link: https://lore.kernel.org/r/20200610224704.27082-2-rikard.falkeborn@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/cardreader/rtsx_usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/cardreader/rtsx_usb.c b/drivers/misc/cardreader/rtsx_usb.c index a328cab11014..59eda55d92a3 100644 --- a/drivers/misc/cardreader/rtsx_usb.c +++ b/drivers/misc/cardreader/rtsx_usb.c @@ -759,7 +759,7 @@ static int rtsx_usb_post_reset(struct usb_interface *intf) return 0; } -static struct usb_device_id rtsx_usb_usb_ids[] = { +static const struct usb_device_id rtsx_usb_usb_ids[] = { { USB_DEVICE(0x0BDA, 0x0129) }, { USB_DEVICE(0x0BDA, 0x0139) }, { USB_DEVICE(0x0BDA, 0x0140) }, -- cgit v1.2.3-59-g8ed1b From 45a2c76283ace79cee5eb7e17a0ee81099700139 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Fri, 19 Jun 2020 19:51:16 +0300 Subject: mei: me: constify the device parameter to the probe quirk The quirk_probe there is no writing to pci device hence we can constify the passed pci_dev pointer. Signed-off-by: Tomas Winkler Link: https://lore.kernel.org/r/20200619165121.2145330-2-tomas.winkler@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/hw-me.c | 6 +++--- drivers/misc/mei/hw-me.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index 7649710a2ab9..f977a150a685 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -1353,7 +1353,7 @@ static const struct mei_hw_ops mei_me_hw_ops = { .read = mei_me_read_slots }; -static bool mei_me_fw_type_nm(struct pci_dev *pdev) +static bool mei_me_fw_type_nm(const struct pci_dev *pdev) { u32 reg; @@ -1366,7 +1366,7 @@ static bool mei_me_fw_type_nm(struct pci_dev *pdev) #define MEI_CFG_FW_NM \ .quirk_probe = mei_me_fw_type_nm -static bool mei_me_fw_type_sps_4(struct pci_dev *pdev) +static bool mei_me_fw_type_sps_4(const struct pci_dev *pdev) { u32 reg; unsigned int devfn; @@ -1395,7 +1395,7 @@ static bool mei_me_fw_type_sps_4(struct pci_dev *pdev) * * Return: true in case of SPS firmware */ -static bool mei_me_fw_type_sps(struct pci_dev *pdev) +static bool mei_me_fw_type_sps(const struct pci_dev *pdev) { u32 reg; u32 fw_type; diff --git a/drivers/misc/mei/hw-me.h b/drivers/misc/mei/hw-me.h index 6a8973649c49..560c8ebb17be 100644 --- a/drivers/misc/mei/hw-me.h +++ b/drivers/misc/mei/hw-me.h @@ -25,7 +25,7 @@ */ struct mei_cfg { const struct mei_fw_status fw_status; - bool (*quirk_probe)(struct pci_dev *pdev); + bool (*quirk_probe)(const struct pci_dev *pdev); size_t dma_size[DMA_DSCR_NUM]; u32 fw_ver_supported:1; u32 hw_trc_supported:1; -- cgit v1.2.3-59-g8ed1b From 4d3c6c8eb9816a0a0cb80becd159cf81a9977ed7 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Fri, 19 Jun 2020 19:51:17 +0300 Subject: mei: me: make mei_me_fw_sku_sps_4() less cryptic Last add mei_me_fw_sku_sps_4() kdoc and add descriptive defines for register name and values. Signed-off-by: Tomas Winkler Link: https://lore.kernel.org/r/20200619165121.2145330-3-tomas.winkler@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/hw-me-regs.h | 2 ++ drivers/misc/mei/hw-me.c | 18 ++++++++++++------ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/drivers/misc/mei/hw-me-regs.h b/drivers/misc/mei/hw-me-regs.h index 7becfc768bbc..79e7bae7dae0 100644 --- a/drivers/misc/mei/hw-me-regs.h +++ b/drivers/misc/mei/hw-me-regs.h @@ -106,6 +106,8 @@ /* Host Firmware Status Registers in PCI Config Space */ #define PCI_CFG_HFS_1 0x40 # define PCI_CFG_HFS_1_D0I3_MSK 0x80000000 +# define PCI_CFG_HFS_1_OPMODE_MSK 0xf0000 /* OP MODE Mask: SPS <= 4.0 */ +# define PCI_CFG_HFS_1_OPMODE_SPS 0xf0000 /* SPS SKU : SPS <= 4.0 */ #define PCI_CFG_HFS_2 0x48 #define PCI_CFG_HFS_3 0x60 # define PCI_CFG_HFS_3_FW_SKU_MSK 0x00000070 diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index f977a150a685..5ca9c678d3a8 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -1366,20 +1366,26 @@ static bool mei_me_fw_type_nm(const struct pci_dev *pdev) #define MEI_CFG_FW_NM \ .quirk_probe = mei_me_fw_type_nm +/** + * mei_me_fw_sku_sps_4() - check for sps 4.0 sku + * + * Read ME FW Status register to check for SPS Firmware. + * The SPS FW is only signaled in the PCI function 0. + * __Note__: Deprecated by SPS 5.0 and newer. + * + * @pdev: pci device + * + * Return: true in case of SPS firmware + */ static bool mei_me_fw_type_sps_4(const struct pci_dev *pdev) { u32 reg; unsigned int devfn; - /* - * Read ME FW Status register to check for SPS Firmware - * The SPS FW is only signaled in pci function 0 - */ devfn = PCI_DEVFN(PCI_SLOT(pdev->devfn), 0); pci_bus_read_config_dword(pdev->bus, devfn, PCI_CFG_HFS_1, ®); trace_mei_pci_cfg_read(&pdev->dev, "PCI_CFG_HFS_1", PCI_CFG_HFS_1, reg); - /* if bits [19:16] = 15, running SPS Firmware */ - return (reg & 0xf0000) == 0xf0000; + return (reg & PCI_CFG_HFS_1_OPMODE_MSK) == PCI_CFG_HFS_1_OPMODE_SPS; } #define MEI_CFG_FW_SPS_4 \ -- cgit v1.2.3-59-g8ed1b From 3fbd1dfe2635190e50534e5e3c21901e639a9068 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Fri, 19 Jun 2020 19:51:18 +0300 Subject: mei: me: add kdoc for mei_me_fw_type_nm() Add kdoc for mei_me_fw_type_nm() function. Signed-off-by: Tomas Winkler Link: https://lore.kernel.org/r/20200619165121.2145330-4-tomas.winkler@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/hw-me.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index 5ca9c678d3a8..c51d3da8f333 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -1353,11 +1353,24 @@ static const struct mei_hw_ops mei_me_hw_ops = { .read = mei_me_read_slots }; +/** + * mei_me_fw_type_nm() - check for nm sku + * + * Read ME FW Status register to check for the Node Manager (NM) Firmware. + * The NM FW is only signaled in PCI function 0. + * __Note__: Deprecated by PCH8 and newer. + * + * @pdev: pci device + * + * Return: true in case of NM firmware + */ static bool mei_me_fw_type_nm(const struct pci_dev *pdev) { u32 reg; + unsigned int devfn; - pci_read_config_dword(pdev, PCI_CFG_HFS_2, ®); + devfn = PCI_DEVFN(PCI_SLOT(pdev->devfn), 0); + pci_bus_read_config_dword(pdev->bus, devfn, PCI_CFG_HFS_2, ®); trace_mei_pci_cfg_read(&pdev->dev, "PCI_CFG_HFS_2", PCI_CFG_HFS_2, reg); /* make sure that bit 9 (NM) is up and bit 10 (DM) is down */ return (reg & 0x600) == 0x200; -- cgit v1.2.3-59-g8ed1b From 372a829862e5f7ca4fd9c816702fb99851740a98 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Fri, 19 Jun 2020 19:51:19 +0300 Subject: mei: me: add MEI device for SPT with ITPS capability Add device ID for Intel Sunrise Point PCH (Skylake) which has Intel Precise Touch & Stylus IPTS (iTouch). Signed-off-by: Tomas Winkler Link: https://lore.kernel.org/r/20200619165121.2145330-5-tomas.winkler@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/hw-me-regs.h | 1 + drivers/misc/mei/pci-me.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/misc/mei/hw-me-regs.h b/drivers/misc/mei/hw-me-regs.h index 79e7bae7dae0..07692a81894c 100644 --- a/drivers/misc/mei/hw-me-regs.h +++ b/drivers/misc/mei/hw-me-regs.h @@ -59,6 +59,7 @@ #define MEI_DEV_ID_SPT 0x9D3A /* Sunrise Point */ #define MEI_DEV_ID_SPT_2 0x9D3B /* Sunrise Point 2 */ +#define MEI_DEV_ID_SPT_3 0x9D3E /* Sunrise Point 3 (iToutch) */ #define MEI_DEV_ID_SPT_H 0xA13A /* Sunrise Point H */ #define MEI_DEV_ID_SPT_H_2 0xA13B /* Sunrise Point H 2 */ diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index 2a3f2fd5df50..07077f690c08 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c @@ -68,6 +68,7 @@ static const struct pci_device_id mei_me_pci_tbl[] = { {MEI_PCI_DEVICE(MEI_DEV_ID_SPT, MEI_ME_PCH8_CFG)}, {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_2, MEI_ME_PCH8_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_3, MEI_ME_PCH8_CFG)}, {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_H, MEI_ME_PCH8_SPS_4_CFG)}, {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_H_2, MEI_ME_PCH8_SPS_4_CFG)}, {MEI_PCI_DEVICE(MEI_DEV_ID_LBG, MEI_ME_PCH12_SPS_4_CFG)}, -- cgit v1.2.3-59-g8ed1b From 4afc339ef0d259d415993d3d96f707a92489d91e Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Fri, 19 Jun 2020 19:51:20 +0300 Subject: mei: me: add MEI device for KBP with ITPS capability Add device ID for Intel Kaby Point PCH (Kabylake) which has Intel Precise Touch & Stylus IPTS (iTouch). Signed-off-by: Tomas Winkler Link: https://lore.kernel.org/r/20200619165121.2145330-6-tomas.winkler@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/hw-me-regs.h | 1 + drivers/misc/mei/pci-me.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/misc/mei/hw-me-regs.h b/drivers/misc/mei/hw-me-regs.h index 07692a81894c..9cf8d8f60cfe 100644 --- a/drivers/misc/mei/hw-me-regs.h +++ b/drivers/misc/mei/hw-me-regs.h @@ -74,6 +74,7 @@ #define MEI_DEV_ID_KBP 0xA2BA /* Kaby Point */ #define MEI_DEV_ID_KBP_2 0xA2BB /* Kaby Point 2 */ +#define MEI_DEV_ID_KBP_3 0xA2BE /* Kaby Point 3 (iTouch) */ #define MEI_DEV_ID_CNP_LP 0x9DE0 /* Cannon Point LP */ #define MEI_DEV_ID_CNP_LP_3 0x9DE4 /* Cannon Point LP 3 (iTouch) */ diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index 07077f690c08..159e40a2505d 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c @@ -82,6 +82,7 @@ static const struct pci_device_id mei_me_pci_tbl[] = { {MEI_PCI_DEVICE(MEI_DEV_ID_KBP, MEI_ME_PCH8_CFG)}, {MEI_PCI_DEVICE(MEI_DEV_ID_KBP_2, MEI_ME_PCH8_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_KBP_3, MEI_ME_PCH8_CFG)}, {MEI_PCI_DEVICE(MEI_DEV_ID_CNP_LP, MEI_ME_PCH12_CFG)}, {MEI_PCI_DEVICE(MEI_DEV_ID_CNP_LP_3, MEI_ME_PCH8_CFG)}, -- cgit v1.2.3-59-g8ed1b From 464e86b4abadfc490f426954b431e2ec6a9d7bd2 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Thu, 25 Jun 2020 13:37:01 -0700 Subject: lkdtm: Avoid more compiler optimizations for bad writes It seems at least Clang is able to throw away writes it knows are destined for read-only memory, which makes things like the WRITE_RO test fail, as the write gets elided. Instead, force the variable to be volatile, and make similar changes through-out other tests in an effort to avoid needing to repeat fixing these kinds of problems. Also includes pr_err() calls in failure paths so that kernel logs are more clear in the failure case. Reported-by: Prasad Sodagudi Suggested-by: Sami Tolvanen Fixes: 9ae113ce5faf ("lkdtm: add tests for additional page permissions") Signed-off-by: Kees Cook Link: https://lore.kernel.org/r/20200625203704.317097-2-keescook@chromium.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/lkdtm/bugs.c | 11 +++++------ drivers/misc/lkdtm/perms.c | 22 +++++++++++++++------- drivers/misc/lkdtm/usercopy.c | 7 +++++-- 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/drivers/misc/lkdtm/bugs.c b/drivers/misc/lkdtm/bugs.c index 736675f0a246..08c70281c380 100644 --- a/drivers/misc/lkdtm/bugs.c +++ b/drivers/misc/lkdtm/bugs.c @@ -118,9 +118,8 @@ noinline void lkdtm_CORRUPT_STACK(void) /* Use default char array length that triggers stack protection. */ char data[8] __aligned(sizeof(void *)); - __lkdtm_CORRUPT_STACK(&data); - - pr_info("Corrupted stack containing char array ...\n"); + pr_info("Corrupting stack containing char array ...\n"); + __lkdtm_CORRUPT_STACK((void *)&data); } /* Same as above but will only get a canary with -fstack-protector-strong */ @@ -131,9 +130,8 @@ noinline void lkdtm_CORRUPT_STACK_STRONG(void) unsigned long *ptr; } data __aligned(sizeof(void *)); - __lkdtm_CORRUPT_STACK(&data); - - pr_info("Corrupted stack containing union ...\n"); + pr_info("Corrupting stack containing union ...\n"); + __lkdtm_CORRUPT_STACK((void *)&data); } void lkdtm_UNALIGNED_LOAD_STORE_WRITE(void) @@ -248,6 +246,7 @@ void lkdtm_ARRAY_BOUNDS(void) kfree(not_checked); kfree(checked); + pr_err("FAIL: survived array bounds overflow!\n"); } void lkdtm_CORRUPT_LIST_ADD(void) diff --git a/drivers/misc/lkdtm/perms.c b/drivers/misc/lkdtm/perms.c index 62f76d506f04..2dede2ef658f 100644 --- a/drivers/misc/lkdtm/perms.c +++ b/drivers/misc/lkdtm/perms.c @@ -57,6 +57,7 @@ static noinline void execute_location(void *dst, bool write) } pr_info("attempting bad execution at %px\n", func); func(); + pr_err("FAIL: func returned\n"); } static void execute_user_location(void *dst) @@ -75,20 +76,22 @@ static void execute_user_location(void *dst) return; pr_info("attempting bad execution at %px\n", func); func(); + pr_err("FAIL: func returned\n"); } void lkdtm_WRITE_RO(void) { - /* Explicitly cast away "const" for the test. */ - unsigned long *ptr = (unsigned long *)&rodata; + /* Explicitly cast away "const" for the test and make volatile. */ + volatile unsigned long *ptr = (unsigned long *)&rodata; pr_info("attempting bad rodata write at %px\n", ptr); *ptr ^= 0xabcd1234; + pr_err("FAIL: survived bad write\n"); } void lkdtm_WRITE_RO_AFTER_INIT(void) { - unsigned long *ptr = &ro_after_init; + volatile unsigned long *ptr = &ro_after_init; /* * Verify we were written to during init. Since an Oops @@ -102,19 +105,21 @@ void lkdtm_WRITE_RO_AFTER_INIT(void) pr_info("attempting bad ro_after_init write at %px\n", ptr); *ptr ^= 0xabcd1234; + pr_err("FAIL: survived bad write\n"); } void lkdtm_WRITE_KERN(void) { size_t size; - unsigned char *ptr; + volatile unsigned char *ptr; size = (unsigned long)do_overwritten - (unsigned long)do_nothing; ptr = (unsigned char *)do_overwritten; pr_info("attempting bad %zu byte write at %px\n", size, ptr); - memcpy(ptr, (unsigned char *)do_nothing, size); + memcpy((void *)ptr, (unsigned char *)do_nothing, size); flush_icache_range((unsigned long)ptr, (unsigned long)(ptr + size)); + pr_err("FAIL: survived bad write\n"); do_overwritten(); } @@ -193,9 +198,11 @@ void lkdtm_ACCESS_USERSPACE(void) pr_info("attempting bad read at %px\n", ptr); tmp = *ptr; tmp += 0xc0dec0de; + pr_err("FAIL: survived bad read\n"); pr_info("attempting bad write at %px\n", ptr); *ptr = tmp; + pr_err("FAIL: survived bad write\n"); vm_munmap(user_addr, PAGE_SIZE); } @@ -203,19 +210,20 @@ void lkdtm_ACCESS_USERSPACE(void) void lkdtm_ACCESS_NULL(void) { unsigned long tmp; - unsigned long *ptr = (unsigned long *)NULL; + volatile unsigned long *ptr = (unsigned long *)NULL; pr_info("attempting bad read at %px\n", ptr); tmp = *ptr; tmp += 0xc0dec0de; + pr_err("FAIL: survived bad read\n"); pr_info("attempting bad write at %px\n", ptr); *ptr = tmp; + pr_err("FAIL: survived bad write\n"); } void __init lkdtm_perms_init(void) { /* Make sure we can write to __ro_after_init values during __init */ ro_after_init |= 0xAA; - } diff --git a/drivers/misc/lkdtm/usercopy.c b/drivers/misc/lkdtm/usercopy.c index e172719dd86d..b833367a45d0 100644 --- a/drivers/misc/lkdtm/usercopy.c +++ b/drivers/misc/lkdtm/usercopy.c @@ -304,19 +304,22 @@ void lkdtm_USERCOPY_KERNEL(void) return; } - pr_info("attempting good copy_to_user from kernel rodata\n"); + pr_info("attempting good copy_to_user from kernel rodata: %px\n", + test_text); if (copy_to_user((void __user *)user_addr, test_text, unconst + sizeof(test_text))) { pr_warn("copy_to_user failed unexpectedly?!\n"); goto free_user; } - pr_info("attempting bad copy_to_user from kernel text\n"); + pr_info("attempting bad copy_to_user from kernel text: %px\n", + vm_mmap); if (copy_to_user((void __user *)user_addr, vm_mmap, unconst + PAGE_SIZE)) { pr_warn("copy_to_user failed, but lacked Oops\n"); goto free_user; } + pr_err("FAIL: survived bad copy_to_user()\n"); free_user: vm_munmap(user_addr, PAGE_SIZE); -- cgit v1.2.3-59-g8ed1b From e12145cf1c3a8077e6d9f575711e38dd7d8a3ebc Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Thu, 25 Jun 2020 13:37:02 -0700 Subject: lkdtm/heap: Avoid edge and middle of slabs Har har, after I moved the slab freelist pointer into the middle of the slab, now it looks like the contents are getting poisoned. Adjust the test to avoid the freelist pointer again. Fixes: 3202fa62fb43 ("slub: relocate freelist pointer to middle of object") Cc: stable@vger.kernel.org Signed-off-by: Kees Cook Link: https://lore.kernel.org/r/20200625203704.317097-3-keescook@chromium.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/lkdtm/heap.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/misc/lkdtm/heap.c b/drivers/misc/lkdtm/heap.c index 3c5cec85edce..1323bc16f113 100644 --- a/drivers/misc/lkdtm/heap.c +++ b/drivers/misc/lkdtm/heap.c @@ -58,11 +58,12 @@ void lkdtm_READ_AFTER_FREE(void) int *base, *val, saw; size_t len = 1024; /* - * The slub allocator uses the first word to store the free - * pointer in some configurations. Use the middle of the - * allocation to avoid running into the freelist + * The slub allocator will use the either the first word or + * the middle of the allocation to store the free pointer, + * depending on configurations. Store in the second word to + * avoid running into the freelist. */ - size_t offset = (len / sizeof(*base)) / 2; + size_t offset = sizeof(*base); base = kmalloc(len, GFP_KERNEL); if (!base) { -- cgit v1.2.3-59-g8ed1b From 4fccc8c0ff740e62343b6de38426a48b345b95f4 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Thu, 25 Jun 2020 13:37:03 -0700 Subject: selftests/lkdtm: Reset WARN_ONCE to avoid false negatives Since we expect to see warnings every time for many tests, just reset the WARN_ONCE flags each time the script runs. Fixes: 46d1a0f03d66 ("selftests/lkdtm: Add tests for LKDTM targets") Signed-off-by: Kees Cook Link: https://lore.kernel.org/r/20200625203704.317097-4-keescook@chromium.org Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/lkdtm/run.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tools/testing/selftests/lkdtm/run.sh b/tools/testing/selftests/lkdtm/run.sh index ee64ff8df8f4..8383eb89d88a 100755 --- a/tools/testing/selftests/lkdtm/run.sh +++ b/tools/testing/selftests/lkdtm/run.sh @@ -8,6 +8,7 @@ # set -e TRIGGER=/sys/kernel/debug/provoke-crash/DIRECT +CLEAR_ONCE=/sys/kernel/debug/clear_warn_once KSELFTEST_SKIP_TEST=4 # Verify we have LKDTM available in the kernel. @@ -67,6 +68,11 @@ cleanup() { } trap cleanup EXIT +# Reset WARN_ONCE counters so we trip it each time this runs. +if [ -w $CLEAR_ONCE ] ; then + echo 1 > $CLEAR_ONCE +fi + # Save existing dmesg so we can detect new content below dmesg > "$DMESG" -- cgit v1.2.3-59-g8ed1b From ae56942c14740c2963222efdc36c667ab19555ef Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Thu, 25 Jun 2020 13:37:04 -0700 Subject: lkdtm: Make arch-specific tests always available I'd like arch-specific tests to XFAIL when on a mismatched architecture so that we can more easily compare test coverage across all systems. Lacking kernel configs or CPU features count as a FAIL, not an XFAIL. Additionally fixes a build failure under 32-bit UML. Fixes: b09511c253e5 ("lkdtm: Add a DOUBLE_FAULT crash type on x86") Fixes: cea23efb4de2 ("lkdtm/bugs: Make double-fault test always available") Fixes: 6cb6982f42cb ("lkdtm: arm64: test kernel pointer authentication") Signed-off-by: Kees Cook Link: https://lore.kernel.org/r/20200625203704.317097-5-keescook@chromium.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/lkdtm/bugs.c | 38 ++++++++++++++++++--------------- drivers/misc/lkdtm/lkdtm.h | 2 -- tools/testing/selftests/lkdtm/tests.txt | 1 + 3 files changed, 22 insertions(+), 19 deletions(-) diff --git a/drivers/misc/lkdtm/bugs.c b/drivers/misc/lkdtm/bugs.c index 08c70281c380..10338800f6be 100644 --- a/drivers/misc/lkdtm/bugs.c +++ b/drivers/misc/lkdtm/bugs.c @@ -13,7 +13,7 @@ #include #include -#ifdef CONFIG_X86_32 +#if IS_ENABLED(CONFIG_X86_32) && !IS_ENABLED(CONFIG_UML) #include #endif @@ -418,7 +418,7 @@ void lkdtm_UNSET_SMEP(void) void lkdtm_DOUBLE_FAULT(void) { -#ifdef CONFIG_X86_32 +#if IS_ENABLED(CONFIG_X86_32) && !IS_ENABLED(CONFIG_UML) /* * Trigger #DF by setting the stack limit to zero. This clobbers * a GDT TLS slot, which is okay because the current task will die @@ -453,38 +453,42 @@ void lkdtm_DOUBLE_FAULT(void) #endif } -#ifdef CONFIG_ARM64_PTR_AUTH +#ifdef CONFIG_ARM64 static noinline void change_pac_parameters(void) { - /* Reset the keys of current task */ - ptrauth_thread_init_kernel(current); - ptrauth_thread_switch_kernel(current); + if (IS_ENABLED(CONFIG_ARM64_PTR_AUTH)) { + /* Reset the keys of current task */ + ptrauth_thread_init_kernel(current); + ptrauth_thread_switch_kernel(current); + } } +#endif -#define CORRUPT_PAC_ITERATE 10 noinline void lkdtm_CORRUPT_PAC(void) { +#ifdef CONFIG_ARM64 +#define CORRUPT_PAC_ITERATE 10 int i; + if (!IS_ENABLED(CONFIG_ARM64_PTR_AUTH)) + pr_err("FAIL: kernel not built with CONFIG_ARM64_PTR_AUTH\n"); + if (!system_supports_address_auth()) { - pr_err("FAIL: arm64 pointer authentication feature not present\n"); + pr_err("FAIL: CPU lacks pointer authentication feature\n"); return; } - pr_info("Change the PAC parameters to force function return failure\n"); + pr_info("changing PAC parameters to force function return failure...\n"); /* - * Pac is a hash value computed from input keys, return address and + * PAC is a hash value computed from input keys, return address and * stack pointer. As pac has fewer bits so there is a chance of * collision, so iterate few times to reduce the collision probability. */ for (i = 0; i < CORRUPT_PAC_ITERATE; i++) change_pac_parameters(); - pr_err("FAIL: %s test failed. Kernel may be unstable from here\n", __func__); -} -#else /* !CONFIG_ARM64_PTR_AUTH */ -noinline void lkdtm_CORRUPT_PAC(void) -{ - pr_err("FAIL: arm64 pointer authentication config disabled\n"); -} + pr_err("FAIL: survived PAC changes! Kernel may be unstable from here\n"); +#else + pr_err("XFAIL: this test is arm64-only\n"); #endif +} diff --git a/drivers/misc/lkdtm/lkdtm.h b/drivers/misc/lkdtm/lkdtm.h index 601a2156a0d4..8878538b2c13 100644 --- a/drivers/misc/lkdtm/lkdtm.h +++ b/drivers/misc/lkdtm/lkdtm.h @@ -31,9 +31,7 @@ void lkdtm_CORRUPT_USER_DS(void); void lkdtm_STACK_GUARD_PAGE_LEADING(void); void lkdtm_STACK_GUARD_PAGE_TRAILING(void); void lkdtm_UNSET_SMEP(void); -#ifdef CONFIG_X86_32 void lkdtm_DOUBLE_FAULT(void); -#endif void lkdtm_CORRUPT_PAC(void); /* lkdtm_heap.c */ diff --git a/tools/testing/selftests/lkdtm/tests.txt b/tools/testing/selftests/lkdtm/tests.txt index 92ca32143ae5..9d266e79c6a2 100644 --- a/tools/testing/selftests/lkdtm/tests.txt +++ b/tools/testing/selftests/lkdtm/tests.txt @@ -14,6 +14,7 @@ STACK_GUARD_PAGE_LEADING STACK_GUARD_PAGE_TRAILING UNSET_SMEP CR4 bits went missing DOUBLE_FAULT +CORRUPT_PAC UNALIGNED_LOAD_STORE_WRITE #OVERWRITE_ALLOCATION Corrupts memory on failure #WRITE_AFTER_FREE Corrupts memory on failure -- cgit v1.2.3-59-g8ed1b From 69b4bf2be22ed6d03cdb5b53e44eda660ab5df9b Mon Sep 17 00:00:00 2001 From: Vaibhav Gupta Date: Mon, 29 Jun 2020 13:45:27 +0530 Subject: cb710/core.c: use generic power management Drivers should not use legacy power management as they have to manage power states and related operations, for the device, themselves. This driver was handling them with the help of PCI helper functions like pci_save/restore_state(), pci_enable/disable_device(), etc. With generic PM, all essentials will be handled by the PCI core. Driver needs to do only device-specific operations. Compile-tested only. Signed-off-by: Vaibhav Gupta Link: https://lore.kernel.org/r/20200629081531.214734-2-vaibhavgupta40@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/cb710/core.c | 28 +++++++--------------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/drivers/misc/cb710/core.c b/drivers/misc/cb710/core.c index b290bc2ee240..55b7ee0e8f93 100644 --- a/drivers/misc/cb710/core.c +++ b/drivers/misc/cb710/core.c @@ -166,37 +166,24 @@ void cb710_set_irq_handler(struct cb710_slot *slot, } EXPORT_SYMBOL_GPL(cb710_set_irq_handler); -#ifdef CONFIG_PM - -static int cb710_suspend(struct pci_dev *pdev, pm_message_t state) +static int __maybe_unused cb710_suspend(struct device *dev_d) { + struct pci_dev *pdev = to_pci_dev(dev_d); struct cb710_chip *chip = pci_get_drvdata(pdev); devm_free_irq(&pdev->dev, pdev->irq, chip); - pci_save_state(pdev); - pci_disable_device(pdev); - if (state.event & PM_EVENT_SLEEP) - pci_set_power_state(pdev, PCI_D3hot); return 0; } -static int cb710_resume(struct pci_dev *pdev) +static int __maybe_unused cb710_resume(struct device *dev_d) { + struct pci_dev *pdev = to_pci_dev(dev_d); struct cb710_chip *chip = pci_get_drvdata(pdev); - int err; - - pci_set_power_state(pdev, PCI_D0); - pci_restore_state(pdev); - err = pcim_enable_device(pdev); - if (err) - return err; return devm_request_irq(&pdev->dev, pdev->irq, cb710_irq_handler, IRQF_SHARED, KBUILD_MODNAME, chip); } -#endif /* CONFIG_PM */ - static int cb710_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { @@ -312,15 +299,14 @@ static const struct pci_device_id cb710_pci_tbl[] = { { 0, } }; +static SIMPLE_DEV_PM_OPS(cb710_pm_ops, cb710_suspend, cb710_resume); + static struct pci_driver cb710_driver = { .name = KBUILD_MODNAME, .id_table = cb710_pci_tbl, .probe = cb710_probe, .remove = cb710_remove_one, -#ifdef CONFIG_PM - .suspend = cb710_suspend, - .resume = cb710_resume, -#endif + .driver.pm = &cb710_pm_ops, }; static int __init cb710_init_module(void) -- cgit v1.2.3-59-g8ed1b From 6bf23661d4a7a105001ebb4410c1c0a17752fac4 Mon Sep 17 00:00:00 2001 From: Vaibhav Gupta Date: Mon, 29 Jun 2020 13:45:28 +0530 Subject: cardreader/rtsx_pcr.c: use generic power management Drivers should not use legacy power management as they have to manage power states and related operations, for the device, themselves. This driver was handling them with the help of PCI helper functions like pci_save/restore_state(), pci_enable/disable_device(), etc. With generic PM, all essentials will be handled by the PCI core. Driver needs to do only device-specific operations. The driver was also using pci_enable_wake(...,..., 0) to disable wake. Use device_wakeup_disable() instead. Compile-tested only. Signed-off-by: Vaibhav Gupta Link: https://lore.kernel.org/r/20200629081531.214734-3-vaibhavgupta40@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/cardreader/rtsx_pcr.c | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/drivers/misc/cardreader/rtsx_pcr.c b/drivers/misc/cardreader/rtsx_pcr.c index 0d5928bc1b6d..ca5212cba1c0 100644 --- a/drivers/misc/cardreader/rtsx_pcr.c +++ b/drivers/misc/cardreader/rtsx_pcr.c @@ -1604,10 +1604,9 @@ static void rtsx_pci_remove(struct pci_dev *pcidev) pci_name(pcidev), (int)pcidev->vendor, (int)pcidev->device); } -#ifdef CONFIG_PM - -static int rtsx_pci_suspend(struct pci_dev *pcidev, pm_message_t state) +static int __maybe_unused rtsx_pci_suspend(struct device *dev_d) { + struct pci_dev *pcidev = to_pci_dev(dev_d); struct pcr_handle *handle; struct rtsx_pcr *pcr; @@ -1623,17 +1622,15 @@ static int rtsx_pci_suspend(struct pci_dev *pcidev, pm_message_t state) rtsx_pci_power_off(pcr, HOST_ENTER_S3); - pci_save_state(pcidev); - pci_enable_wake(pcidev, pci_choose_state(pcidev, state), 0); - pci_disable_device(pcidev); - pci_set_power_state(pcidev, pci_choose_state(pcidev, state)); + device_wakeup_disable(dev_d); mutex_unlock(&pcr->pcr_mutex); return 0; } -static int rtsx_pci_resume(struct pci_dev *pcidev) +static int __maybe_unused rtsx_pci_resume(struct device *dev_d) { + struct pci_dev *pcidev = to_pci_dev(dev_d); struct pcr_handle *handle; struct rtsx_pcr *pcr; int ret = 0; @@ -1645,11 +1642,6 @@ static int rtsx_pci_resume(struct pci_dev *pcidev) mutex_lock(&pcr->pcr_mutex); - pci_set_power_state(pcidev, PCI_D0); - pci_restore_state(pcidev); - ret = pci_enable_device(pcidev); - if (ret) - goto out; pci_set_master(pcidev); ret = rtsx_pci_write_register(pcr, HOST_SLEEP_STATE, 0x03, 0x00); @@ -1667,6 +1659,8 @@ out: return ret; } +#ifdef CONFIG_PM + static void rtsx_pci_shutdown(struct pci_dev *pcidev) { struct pcr_handle *handle; @@ -1686,19 +1680,18 @@ static void rtsx_pci_shutdown(struct pci_dev *pcidev) #else /* CONFIG_PM */ -#define rtsx_pci_suspend NULL -#define rtsx_pci_resume NULL #define rtsx_pci_shutdown NULL #endif /* CONFIG_PM */ +static SIMPLE_DEV_PM_OPS(rtsx_pci_pm_ops, rtsx_pci_suspend, rtsx_pci_resume); + static struct pci_driver rtsx_pci_driver = { .name = DRV_NAME_RTSX_PCI, .id_table = rtsx_pci_ids, .probe = rtsx_pci_probe, .remove = rtsx_pci_remove, - .suspend = rtsx_pci_suspend, - .resume = rtsx_pci_resume, + .driver.pm = &rtsx_pci_pm_ops, .shutdown = rtsx_pci_shutdown, }; module_pci_driver(rtsx_pci_driver); -- cgit v1.2.3-59-g8ed1b From ff249c1c70a665fc7a64e345aa251bc238616a34 Mon Sep 17 00:00:00 2001 From: Vaibhav Gupta Date: Mon, 29 Jun 2020 13:45:29 +0530 Subject: misc/tifm_7xx1.c: use generic power management Drivers should not use legacy power management as they have to manage power states and related operations, for the device, themselves. This driver was handling them with the help of PCI helper functions like pci_save/restore_state(), pci_enable/disable_device(), etc. With generic PM, all essentials will be handled by the PCI core. Driver needs to do only device-specific operations. The driver was also using pci_enable_wake(...,..., 0) to disable wake. Use device_wakeup_disable() instead. Compile-tested only. Signed-off-by: Vaibhav Gupta Link: https://lore.kernel.org/r/20200629081531.214734-4-vaibhavgupta40@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/tifm_7xx1.c | 30 ++++++++---------------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/drivers/misc/tifm_7xx1.c b/drivers/misc/tifm_7xx1.c index e6b40aa8fb42..228f2eb1d476 100644 --- a/drivers/misc/tifm_7xx1.c +++ b/drivers/misc/tifm_7xx1.c @@ -207,10 +207,9 @@ static void tifm_7xx1_switch_media(struct work_struct *work) spin_unlock_irqrestore(&fm->lock, flags); } -#ifdef CONFIG_PM - -static int tifm_7xx1_suspend(struct pci_dev *dev, pm_message_t state) +static int __maybe_unused tifm_7xx1_suspend(struct device *dev_d) { + struct pci_dev *dev = to_pci_dev(dev_d); struct tifm_adapter *fm = pci_get_drvdata(dev); int cnt; @@ -221,15 +220,13 @@ static int tifm_7xx1_suspend(struct pci_dev *dev, pm_message_t state) tifm_7xx1_sock_power_off(fm->sockets[cnt]->addr); } - pci_save_state(dev); - pci_enable_wake(dev, pci_choose_state(dev, state), 0); - pci_disable_device(dev); - pci_set_power_state(dev, pci_choose_state(dev, state)); + device_wakeup_disable(dev_d); return 0; } -static int tifm_7xx1_resume(struct pci_dev *dev) +static int __maybe_unused tifm_7xx1_resume(struct device *dev_d) { + struct pci_dev *dev = to_pci_dev(dev_d); struct tifm_adapter *fm = pci_get_drvdata(dev); int rc; unsigned long timeout; @@ -242,11 +239,6 @@ static int tifm_7xx1_resume(struct pci_dev *dev) if (WARN_ON(fm->num_sockets > ARRAY_SIZE(new_ids))) return -ENXIO; - pci_set_power_state(dev, PCI_D0); - pci_restore_state(dev); - rc = pci_enable_device(dev); - if (rc) - return rc; pci_set_master(dev); dev_dbg(&dev->dev, "resuming host\n"); @@ -297,13 +289,6 @@ static int tifm_7xx1_resume(struct pci_dev *dev) return 0; } -#else - -#define tifm_7xx1_suspend NULL -#define tifm_7xx1_resume NULL - -#endif /* CONFIG_PM */ - static int tifm_7xx1_dummy_has_ms_pif(struct tifm_adapter *fm, struct tifm_dev *sock) { @@ -424,13 +409,14 @@ static const struct pci_device_id tifm_7xx1_pci_tbl[] = { { } }; +static SIMPLE_DEV_PM_OPS(tifm_7xx1_pm_ops, tifm_7xx1_suspend, tifm_7xx1_resume); + static struct pci_driver tifm_7xx1_driver = { .name = DRIVER_NAME, .id_table = tifm_7xx1_pci_tbl, .probe = tifm_7xx1_probe, .remove = tifm_7xx1_remove, - .suspend = tifm_7xx1_suspend, - .resume = tifm_7xx1_resume, + .driver.pm = &tifm_7xx1_pm_ops, }; module_pci_driver(tifm_7xx1_driver); -- cgit v1.2.3-59-g8ed1b From 6bbf52566bf7cb26546ec9d9aa76a3cb80d4eb18 Mon Sep 17 00:00:00 2001 From: Vaibhav Gupta Date: Mon, 29 Jun 2020 13:45:30 +0530 Subject: misc/phantom.c: use generic power management With the support of generic PM callbacks, drivers no longer need to use legacy .suspend() and .resume() in which they had to maintain PCI states changes and device's power state themselves. All required operations are done by PCI core. Driver needs to do only device-specific operations. Compile-tested only. Signed-off-by: Vaibhav Gupta Link: https://lore.kernel.org/r/20200629081531.214734-5-vaibhavgupta40@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/phantom.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/drivers/misc/phantom.c b/drivers/misc/phantom.c index 6a5ed0e25ff1..ce72e46a2e73 100644 --- a/drivers/misc/phantom.c +++ b/drivers/misc/phantom.c @@ -457,31 +457,26 @@ static void phantom_remove(struct pci_dev *pdev) pci_disable_device(pdev); } -#ifdef CONFIG_PM -static int phantom_suspend(struct pci_dev *pdev, pm_message_t state) +static int __maybe_unused phantom_suspend(struct device *dev_d) { - struct phantom_device *dev = pci_get_drvdata(pdev); + struct phantom_device *dev = dev_get_drvdata(dev_d); iowrite32(0, dev->caddr + PHN_IRQCTL); ioread32(dev->caddr + PHN_IRQCTL); /* PCI posting */ - synchronize_irq(pdev->irq); + synchronize_irq(to_pci_dev(dev_d)->irq); return 0; } -static int phantom_resume(struct pci_dev *pdev) +static int __maybe_unused phantom_resume(struct device *dev_d) { - struct phantom_device *dev = pci_get_drvdata(pdev); + struct phantom_device *dev = dev_get_drvdata(dev_d); iowrite32(0, dev->caddr + PHN_IRQCTL); return 0; } -#else -#define phantom_suspend NULL -#define phantom_resume NULL -#endif static struct pci_device_id phantom_pci_tbl[] = { { .vendor = PCI_VENDOR_ID_PLX, .device = PCI_DEVICE_ID_PLX_9050, @@ -491,13 +486,14 @@ static struct pci_device_id phantom_pci_tbl[] = { }; MODULE_DEVICE_TABLE(pci, phantom_pci_tbl); +static SIMPLE_DEV_PM_OPS(phantom_pm_ops, phantom_suspend, phantom_resume); + static struct pci_driver phantom_pci_driver = { .name = "phantom", .id_table = phantom_pci_tbl, .probe = phantom_probe, .remove = phantom_remove, - .suspend = phantom_suspend, - .resume = phantom_resume + .driver.pm = &phantom_pm_ops, }; static CLASS_ATTR_STRING(version, 0444, PHANTOM_VERSION); -- cgit v1.2.3-59-g8ed1b From 34afa1d657d4742c9bce4a4c197ebf61b4af1b9e Mon Sep 17 00:00:00 2001 From: Vaibhav Gupta Date: Mon, 29 Jun 2020 13:45:31 +0530 Subject: misc/pch_phub.c: use generic power management Drivers should not use legacy power management as they have to manage power states and related operations, for the device, themselves. This driver was handling them with the help of PCI helper functions like pci_save/restore_state(), pci_enable/disable_device(), etc. With generic PM, all essentials will be handled by the PCI core. Driver needs to do only device-specific operations. The driver was also using pci_enable_wake(...,..., 0) to disable wake. Use device_wakeup_disable() instead. It was also saving device register configuration using pch_phub_save/restore_reg_conf() which is not recommended. Compile-tested only. Signed-off-by: Vaibhav Gupta Link: https://lore.kernel.org/r/20200629081531.214734-6-vaibhavgupta40@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/pch_phub.c | 48 +++++++++--------------------------------------- 1 file changed, 9 insertions(+), 39 deletions(-) diff --git a/drivers/misc/pch_phub.c b/drivers/misc/pch_phub.c index 60828af7506a..9321d4239932 100644 --- a/drivers/misc/pch_phub.c +++ b/drivers/misc/pch_phub.c @@ -147,9 +147,8 @@ static void pch_phub_read_modify_write_reg(struct pch_phub_reg *chip, iowrite32(((ioread32(reg_addr) & ~mask)) | data, reg_addr); } -#ifdef CONFIG_PM /* pch_phub_save_reg_conf - saves register configuration */ -static void pch_phub_save_reg_conf(struct pci_dev *pdev) +static void __maybe_unused pch_phub_save_reg_conf(struct pci_dev *pdev) { unsigned int i; struct pch_phub_reg *chip = pci_get_drvdata(pdev); @@ -210,7 +209,7 @@ static void pch_phub_save_reg_conf(struct pci_dev *pdev) } /* pch_phub_restore_reg_conf - restore register configuration */ -static void pch_phub_restore_reg_conf(struct pci_dev *pdev) +static void __maybe_unused pch_phub_restore_reg_conf(struct pci_dev *pdev) { unsigned int i; struct pch_phub_reg *chip = pci_get_drvdata(pdev); @@ -270,7 +269,6 @@ static void pch_phub_restore_reg_conf(struct pci_dev *pdev) if ((chip->ioh_type == 2) || (chip->ioh_type == 4)) iowrite32(chip->funcsel_reg, p + FUNCSEL_REG_OFFSET); } -#endif /** * pch_phub_read_serial_rom() - Reading Serial ROM @@ -835,48 +833,19 @@ static void pch_phub_remove(struct pci_dev *pdev) kfree(chip); } -#ifdef CONFIG_PM - -static int pch_phub_suspend(struct pci_dev *pdev, pm_message_t state) +static int __maybe_unused pch_phub_suspend(struct device *dev_d) { - int ret; - - pch_phub_save_reg_conf(pdev); - ret = pci_save_state(pdev); - if (ret) { - dev_err(&pdev->dev, - " %s -pci_save_state returns %d\n", __func__, ret); - return ret; - } - pci_enable_wake(pdev, PCI_D3hot, 0); - pci_disable_device(pdev); - pci_set_power_state(pdev, pci_choose_state(pdev, state)); + device_wakeup_disable(dev_d); return 0; } -static int pch_phub_resume(struct pci_dev *pdev) +static int __maybe_unused pch_phub_resume(struct device *dev_d) { - int ret; - - pci_set_power_state(pdev, PCI_D0); - pci_restore_state(pdev); - ret = pci_enable_device(pdev); - if (ret) { - dev_err(&pdev->dev, - "%s-pci_enable_device failed(ret=%d) ", __func__, ret); - return ret; - } - - pci_enable_wake(pdev, PCI_D3hot, 0); - pch_phub_restore_reg_conf(pdev); + device_wakeup_disable(dev_d); return 0; } -#else -#define pch_phub_suspend NULL -#define pch_phub_resume NULL -#endif /* CONFIG_PM */ static const struct pci_device_id pch_phub_pcidev_id[] = { { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_PCH1_PHUB), 1, }, @@ -888,13 +857,14 @@ static const struct pci_device_id pch_phub_pcidev_id[] = { }; MODULE_DEVICE_TABLE(pci, pch_phub_pcidev_id); +static SIMPLE_DEV_PM_OPS(pch_phub_pm_ops, pch_phub_suspend, pch_phub_resume); + static struct pci_driver pch_phub_driver = { .name = "pch_phub", .id_table = pch_phub_pcidev_id, .probe = pch_phub_probe, .remove = pch_phub_remove, - .suspend = pch_phub_suspend, - .resume = pch_phub_resume + .driver.pm = &pch_phub_pm_ops, }; module_pci_driver(pch_phub_driver); -- cgit v1.2.3-59-g8ed1b From b23d5151f301dcbc3c6a69ea80901d9566d910d7 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Fri, 26 Jun 2020 14:05:16 +0100 Subject: misc: c2port: core: Ensure source size does not equal destination size in strncpy() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We need to ensure there's a place for the NULL terminator. Fixes the following W=1 warning(s): In file included from include/linux/bitmap.h:9, from include/linux/nodemask.h:95, from include/linux/mmzone.h:17, from include/linux/gfp.h:6, from include/linux/umh.h:4, from include/linux/kmod.h:9, from include/linux/module.h:16, from drivers/misc/c2port/core.c:9: In function ‘strncpy’, inlined from ‘c2port_device_register’ at drivers/misc/c2port/core.c:926:2: include/linux/string.h:297:30: warning: ‘__builtin_strncpy’ specified bound 32 equals destination size [-Wstringop-truncation] 297 | #define __underlying_strncpy __builtin_strncpy | ^ include/linux/string.h:307:9: note: in expansion of macro ‘__underlying_strncpy’ 307 | return __underlying_strncpy(p, q, size); | ^~~~~~~~~~~~~~~~~~~~ Cc: Rodolfo Giometti Cc: "Eurotech S.p.A" Acked-by: Rodolfo Giometti Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20200626130525.389469-2-lee.jones@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/c2port/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/c2port/core.c b/drivers/misc/c2port/core.c index 33bba1802289..80d87e8a0bea 100644 --- a/drivers/misc/c2port/core.c +++ b/drivers/misc/c2port/core.c @@ -923,7 +923,7 @@ struct c2port_device *c2port_device_register(char *name, } dev_set_drvdata(c2dev->dev, c2dev); - strncpy(c2dev->name, name, C2PORT_NAME_LEN); + strncpy(c2dev->name, name, C2PORT_NAME_LEN - 1); c2dev->ops = ops; mutex_init(&c2dev->mutex); -- cgit v1.2.3-59-g8ed1b From 7e8eebef1c3afe375623e76b8741d48b1f6eb13e Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Fri, 26 Jun 2020 14:05:17 +0100 Subject: misc: ti-st: st_core: Tidy-up bespoke commentry If it's still in use and worth the effort, it sure looks like this driver could do with a good scrub (clean). This patch conserns itself with the non-standard comments located thoughout the file. It also fixes the following W=1 warnings by demoting the kerneldoc function headers to standard comments, since there doesn't appear to be a requirement for the function args to be documented: drivers/misc/ti-st/st_core.c:132: warning: Function parameter or member 'st_gdata' not described in 'st_reg_complete' drivers/misc/ti-st/st_core.c:132: warning: Function parameter or member 'err' not described in 'st_reg_complete' drivers/misc/ti-st/st_core.c:197: warning: Function parameter or member 'st_gdata' not described in 'st_wakeup_ack' drivers/misc/ti-st/st_core.c:197: warning: Function parameter or member 'cmd' not described in 'st_wakeup_ack' drivers/misc/ti-st/st_core.c:226: warning: Function parameter or member 'disc_data' not described in 'st_int_recv' drivers/misc/ti-st/st_core.c:226: warning: Function parameter or member 'data' not described in 'st_int_recv' drivers/misc/ti-st/st_core.c:226: warning: Function parameter or member 'count' not described in 'st_int_recv' drivers/misc/ti-st/st_core.c:387: warning: Function parameter or member 'st_gdata' not described in 'st_int_dequeue' drivers/misc/ti-st/st_core.c:409: warning: Function parameter or member 'st_gdata' not described in 'st_int_enqueue' drivers/misc/ti-st/st_core.c:409: warning: Function parameter or member 'skb' not described in 'st_int_enqueue' Cc: Pavan Savoy Cc: Naveen Jain Acked-by: Arnd Bergmann Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20200626130525.389469-3-lee.jones@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/ti-st/st_core.c | 79 +++++++++++++++++++++++++++----------------- 1 file changed, 49 insertions(+), 30 deletions(-) diff --git a/drivers/misc/ti-st/st_core.c b/drivers/misc/ti-st/st_core.c index 14136d2cc8f9..f4ddd1e67015 100644 --- a/drivers/misc/ti-st/st_core.c +++ b/drivers/misc/ti-st/st_core.c @@ -18,7 +18,8 @@ extern void st_kim_recv(void *, const unsigned char *, long); void st_int_recv(void *, const unsigned char *, long); -/* function pointer pointing to either, +/* + * function pointer pointing to either, * st_kim_recv during registration to receive fw download responses * st_int_recv after registration to receive proto stack responses */ @@ -60,7 +61,8 @@ int st_get_uart_wr_room(struct st_data_s *st_gdata) return tty->ops->write_room(tty); } -/* can be called in from +/* + * can be called in from * -- KIM (during fw download) * -- ST Core (during st_write) * @@ -100,7 +102,8 @@ static void st_send_frame(unsigned char chnl_id, struct st_data_s *st_gdata) kfree_skb(st_gdata->rx_skb); return; } - /* this cannot fail + /* + * this cannot fail * this shouldn't take long * - should be just skb_queue_tail for the * protocol stack driver @@ -121,9 +124,8 @@ static void st_send_frame(unsigned char chnl_id, struct st_data_s *st_gdata) return; } -/** - * st_reg_complete - - * to call registration complete callbacks +/* + * st_reg_complete - to call registration complete callbacks * of all protocol stack drivers * This function is being called with spin lock held, protocol drivers are * only expected to complete their waits and do nothing more than that. @@ -156,21 +158,24 @@ static inline int st_check_data_len(struct st_data_s *st_gdata, pr_debug("len %d room %d", len, room); if (!len) { - /* Received packet has only packet header and + /* + * Received packet has only packet header and * has zero length payload. So, ask ST CORE to * forward the packet to protocol driver (BT/FM/GPS) */ st_send_frame(chnl_id, st_gdata); } else if (len > room) { - /* Received packet's payload length is larger. + /* + * Received packet's payload length is larger. * We can't accommodate it in created skb. */ pr_err("Data length is too large len %d room %d", len, room); kfree_skb(st_gdata->rx_skb); } else { - /* Packet header has non-zero payload length and + /* + * Packet header has non-zero payload length and * we have enough space in created skb. Lets read * payload data */ st_gdata->rx_state = ST_W4_DATA; @@ -178,8 +183,7 @@ static inline int st_check_data_len(struct st_data_s *st_gdata, return len; } - /* Change ST state to continue to process next - * packet */ + /* Change ST state to continue to process next packet */ st_gdata->rx_state = ST_W4_PACKET_TYPE; st_gdata->rx_skb = NULL; st_gdata->rx_count = 0; @@ -188,7 +192,7 @@ static inline int st_check_data_len(struct st_data_s *st_gdata, return 0; } -/** +/* * st_wakeup_ack - internal function for action when wake-up ack * received */ @@ -199,7 +203,8 @@ static inline void st_wakeup_ack(struct st_data_s *st_gdata, unsigned long flags = 0; spin_lock_irqsave(&st_gdata->lock, flags); - /* de-Q from waitQ and Q in txQ now that the + /* + * de-Q from waitQ and Q in txQ now that the * chip is awake */ while ((waiting_skb = skb_dequeue(&st_gdata->tx_waitq))) @@ -213,7 +218,7 @@ static inline void st_wakeup_ack(struct st_data_s *st_gdata, st_tx_wakeup(st_gdata); } -/** +/* * st_int_recv - ST's internal receive function. * Decodes received RAW data and forwards to corresponding * client drivers (Bluetooth,FM,GPS..etc). @@ -262,8 +267,10 @@ void st_int_recv(void *disc_data, /* Waiting for complete packet ? */ case ST_W4_DATA: pr_debug("Complete pkt received"); - /* Ask ST CORE to forward - * the packet to protocol driver */ + /* + * Ask ST CORE to forward + * the packet to protocol driver + */ st_send_frame(st_gdata->rx_chnl, st_gdata); st_gdata->rx_state = ST_W4_PACKET_TYPE; @@ -276,7 +283,7 @@ void st_int_recv(void *disc_data, &st_gdata->rx_skb->data [proto->offset_len_in_hdr]; pr_debug("plen pointing to %x\n", *plen); - if (proto->len_size == 1)/* 1 byte len field */ + if (proto->len_size == 1) /* 1 byte len field */ payload_len = *(unsigned char *)plen; else if (proto->len_size == 2) payload_len = @@ -294,18 +301,23 @@ void st_int_recv(void *disc_data, } /* end of if rx_count */ - /* Check first byte of packet and identify module - * owner (BT/FM/GPS) */ + + /* + * Check first byte of packet and identify module + * owner (BT/FM/GPS) + */ switch (*ptr) { case LL_SLEEP_IND: case LL_SLEEP_ACK: case LL_WAKE_UP_IND: pr_debug("PM packet"); - /* this takes appropriate action based on + /* + * this takes appropriate action based on * sleep state received -- */ st_ll_sleep_state(st_gdata, *ptr); - /* if WAKEUP_IND collides copy from waitq to txq + /* + * if WAKEUP_IND collides copy from waitq to txq * and assume chip awake */ spin_unlock_irqrestore(&st_gdata->lock, flags); @@ -331,7 +343,8 @@ void st_int_recv(void *disc_data, default: type = *ptr; - /* Default case means non-HCILL packets, + /* + * Default case means non-HCILL packets, * possibilities are packets for: * (a) valid protocol - Supported Protocols within * the ST_MAX_CHANNELS. @@ -377,7 +390,7 @@ done: return; } -/** +/* * st_int_dequeue - internal de-Q function. * If the previous data set was not written * completely, return that skb which has the pending data. @@ -396,7 +409,7 @@ static struct sk_buff *st_int_dequeue(struct st_data_s *st_gdata) return skb_dequeue(&st_gdata->txq); } -/** +/* * st_int_enqueue - internal Q-ing function. * Will either Q the skb to txq or the tx_waitq * depending on the ST LL state. @@ -561,7 +574,8 @@ long st_register(struct st_proto_s *new_proto) /* release lock previously held - re-locked below */ spin_unlock_irqrestore(&st_gdata->lock, flags); - /* this may take a while to complete + /* + * this may take a while to complete * since it involves BT fw download */ err = st_kim_start(st_gdata->kim_data); @@ -583,7 +597,8 @@ long st_register(struct st_proto_s *new_proto) clear_bit(ST_REG_IN_PROGRESS, &st_gdata->st_state); st_recv = st_int_recv; - /* this is where all pending registration + /* + * this is where all pending registration * are signalled to be complete by calling callback functions */ if ((st_gdata->protos_registered != ST_EMPTY) && @@ -593,7 +608,8 @@ long st_register(struct st_proto_s *new_proto) } clear_bit(ST_REG_PENDING, &st_gdata->st_state); - /* check for already registered once more, + /* + * check for already registered once more, * since the above check is old */ if (st_gdata->is_registered[new_proto->chnl_id] == true) { @@ -622,7 +638,8 @@ long st_register(struct st_proto_s *new_proto) } EXPORT_SYMBOL_GPL(st_register); -/* to unregister a protocol - +/* + * to unregister a protocol - * to be called from protocol stack driver */ long st_unregister(struct st_proto_s *proto) @@ -742,7 +759,8 @@ static void st_tty_close(struct tty_struct *tty) pr_info("%s ", __func__); - /* TODO: + /* + * TODO: * if a protocol has been registered & line discipline * un-installed for some reason - what should be done ? */ @@ -795,7 +813,8 @@ static void st_tty_receive(struct tty_struct *tty, const unsigned char *data, pr_debug("done %s", __func__); } -/* wake-up function called in from the TTY layer +/* + * wake-up function called in from the TTY layer * inside the internal wakeup function will be called */ static void st_tty_wakeup(struct tty_struct *tty) -- cgit v1.2.3-59-g8ed1b From 3caf1b48397f2de0dab88d5b2daf002159ed30bd Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Fri, 26 Jun 2020 14:05:18 +0100 Subject: misc: ti-st: st_kim: Tidy-up bespoke commentry If it's still in use and worth the effort, it sure looks like this driver could do with a good scrub (clean). This patch conserns itself with the non-standard comments located thoughout the file. It also fixes the following W=1 warnings by demoting the kerneldoc function headers to standard comments, since there doesn't appear to be a requirement for the function args to be documented: /drivers/misc/ti-st/st_kim.c:42: warning: Function parameter or member 'id' not described in 'st_get_plat_device' /drivers/misc/ti-st/st_kim.c:53: warning: Function parameter or member 'kim_gdata' not described in 'validate_firmware_response' /drivers/misc/ti-st/st_kim.c:126: warning: Function parameter or member 'kim_gdata' not described in 'kim_int_recv' /drivers/misc/ti-st/st_kim.c:126: warning: Function parameter or member 'data' not described in 'kim_int_recv' /drivers/misc/ti-st/st_kim.c:126: warning: Function parameter or member 'count' not described in 'kim_int_recv' /drivers/misc/ti-st/st_kim.c:272: warning: Function parameter or member 'kim_gdata' not described in 'download_firmware' /drivers/misc/ti-st/st_kim.c:445: warning: Function parameter or member 'kim_data' not described in 'st_kim_start' /drivers/misc/ti-st/st_kim.c:509: warning: Function parameter or member 'kim_data' not described in 'st_kim_stop' /drivers/misc/ti-st/st_kim.c:661: warning: Function parameter or member 'core_data' not described in 'st_kim_ref' /drivers/misc/ti-st/st_kim.c:661: warning: Function parameter or member 'id' not described in 'st_kim_ref' Cc: Pavan Savoy Acked-by: Arnd Bergmann Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20200626130525.389469-4-lee.jones@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/ti-st/st_kim.c | 71 ++++++++++++++++++++++++++++----------------- 1 file changed, 44 insertions(+), 27 deletions(-) diff --git a/drivers/misc/ti-st/st_kim.c b/drivers/misc/ti-st/st_kim.c index a36ed1ff5967..f2f6cab97c08 100644 --- a/drivers/misc/ti-st/st_kim.c +++ b/drivers/misc/ti-st/st_kim.c @@ -30,7 +30,7 @@ static struct platform_device *st_kim_devices[MAX_ST_DEVICES]; /**********************************************************************/ /* internal functions */ -/** +/* * st_get_plat_device - * function which returns the reference to the platform device * requested by id. As of now only 1 such device exists (id=0) @@ -43,7 +43,7 @@ static struct platform_device *st_get_plat_device(int id) return st_kim_devices[id]; } -/** +/* * validate_firmware_response - * function to return whether the firmware response was proper * in case of error don't complete so that waiting for proper @@ -55,7 +55,8 @@ static void validate_firmware_response(struct kim_data_s *kim_gdata) if (!skb) return; - /* these magic numbers are the position in the response buffer which + /* + * these magic numbers are the position in the response buffer which * allows us to distinguish whether the response is for the read * version info. command */ @@ -79,7 +80,8 @@ static void validate_firmware_response(struct kim_data_s *kim_gdata) kfree_skb(skb); } -/* check for data len received inside kim_int_recv +/* + * check for data len received inside kim_int_recv * most often hit the last case to update state to waiting for data */ static inline int kim_check_data_len(struct kim_data_s *kim_gdata, int len) @@ -91,14 +93,16 @@ static inline int kim_check_data_len(struct kim_data_s *kim_gdata, int len) if (!len) { validate_firmware_response(kim_gdata); } else if (len > room) { - /* Received packet's payload length is larger. + /* + * Received packet's payload length is larger. * We can't accommodate it in created skb. */ pr_err("Data length is too large len %d room %d", len, room); kfree_skb(kim_gdata->rx_skb); } else { - /* Packet header has non-zero payload length and + /* + * Packet header has non-zero payload length and * we have enough space in created skb. Lets read * payload data */ kim_gdata->rx_state = ST_W4_DATA; @@ -106,8 +110,10 @@ static inline int kim_check_data_len(struct kim_data_s *kim_gdata, int len) return len; } - /* Change ST LL state to continue to process next - * packet */ + /* + * Change ST LL state to continue to process next + * packet + */ kim_gdata->rx_state = ST_W4_PACKET_TYPE; kim_gdata->rx_skb = NULL; kim_gdata->rx_count = 0; @@ -115,7 +121,7 @@ static inline int kim_check_data_len(struct kim_data_s *kim_gdata, int len) return 0; } -/** +/* * kim_int_recv - receive function called during firmware download * firmware download responses on different UART drivers * have been observed to come in bursts of different @@ -216,7 +222,8 @@ static long read_local_version(struct kim_data_s *kim_gdata, char *bts_scr_name) return timeout ? -ERESTARTSYS : -ETIMEDOUT; } reinit_completion(&kim_gdata->kim_rcvd); - /* the positions 12 & 13 in the response buffer provide with the + /* + * the positions 12 & 13 in the response buffer provide with the * chip, major & minor numbers */ @@ -263,7 +270,7 @@ static void skip_change_remote_baud(unsigned char **ptr, long *len) } } -/** +/* * download_firmware - * internal function which parses through the .bts firmware * script file intreprets SEND, DELAY actions only as of now @@ -295,7 +302,8 @@ static long download_firmware(struct kim_data_s *kim_gdata) } ptr = (void *)kim_gdata->fw_entry->data; len = kim_gdata->fw_entry->size; - /* bts_header to remove out magic number and + /* + * bts_header to remove out magic number and * version */ ptr += sizeof(struct bts_header); @@ -313,8 +321,10 @@ static long download_firmware(struct kim_data_s *kim_gdata) if (unlikely (((struct hci_command *)action_ptr)->opcode == 0xFF36)) { - /* ignore remote change - * baud rate HCI VS command */ + /* + * ignore remote change + * baud rate HCI VS command + */ pr_warn("change remote baud" " rate command in firmware"); skip_change_remote_baud(&ptr, &len); @@ -346,7 +356,8 @@ static long download_firmware(struct kim_data_s *kim_gdata) release_firmware(kim_gdata->fw_entry); return -ETIMEDOUT; } - /* reinit completion before sending for the + /* + * reinit completion before sending for the * relevant wait */ reinit_completion(&kim_gdata->kim_rcvd); @@ -418,14 +429,16 @@ void st_kim_recv(void *disc_data, const unsigned char *data, long count) struct st_data_s *st_gdata = (struct st_data_s *)disc_data; struct kim_data_s *kim_gdata = st_gdata->kim_data; - /* proceed to gather all data and distinguish read fw version response + /* + * proceed to gather all data and distinguish read fw version response * from other fw responses when data gathering is complete */ kim_int_recv(kim_gdata, data, count); return; } -/* to signal completion of line discipline installation +/* + * to signal completion of line discipline installation * called from ST Core, upon tty_open */ void st_kim_complete(void *kim_data) @@ -434,7 +447,7 @@ void st_kim_complete(void *kim_data) complete(&kim_gdata->ldisc_installed); } -/** +/* * st_kim_start - called from ST Core upon 1st registration * This involves toggling the chip enable gpio, reading * the firmware version from chip, forming the fw file name @@ -472,8 +485,10 @@ long st_kim_start(void *kim_data) err = wait_for_completion_interruptible_timeout( &kim_gdata->ldisc_installed, msecs_to_jiffies(LDISC_TIME)); if (!err) { - /* ldisc installation timeout, - * flush uart, power cycle BT_EN */ + /* + * ldisc installation timeout, + * flush uart, power cycle BT_EN + */ pr_err("ldisc installation timeout"); err = st_kim_stop(kim_gdata); continue; @@ -482,8 +497,10 @@ long st_kim_start(void *kim_data) pr_info("line discipline installed"); err = download_firmware(kim_gdata); if (err != 0) { - /* ldisc installed but fw download failed, - * flush uart & power cycle BT_EN */ + /* + * ldisc installed but fw download failed, + * flush uart & power cycle BT_EN + */ pr_err("download firmware failed"); err = st_kim_stop(kim_gdata); continue; @@ -495,7 +512,7 @@ long st_kim_start(void *kim_data) return err; } -/** +/* * st_kim_stop - stop communication with chip. * This can be called from ST Core/KIM, on the- * (a) last un-register when chip need not be powered there-after, @@ -650,7 +667,7 @@ static const struct attribute_group uim_attr_grp = { .attrs = uim_attrs, }; -/** +/* * st_kim_ref - reference the core's data * This references the per-ST platform device in the arch/xx/ * board-xx.c file. @@ -729,8 +746,7 @@ static int kim_probe(struct platform_device *pdev) pr_err(" unable to configure gpio %d", kim_gdata->nshutdown); goto err_sysfs_group; } - /* get reference of pdev for request_firmware - */ + /* get reference of pdev for request_firmware */ kim_gdata->kim_pdev = pdev; init_completion(&kim_gdata->kim_rcvd); init_completion(&kim_gdata->ldisc_installed); @@ -772,7 +788,8 @@ static int kim_remove(struct platform_device *pdev) kim_gdata = platform_get_drvdata(pdev); - /* Free the Bluetooth/FM/GPIO + /* + * Free the Bluetooth/FM/GPIO * nShutdown gpio from the system */ gpio_free(pdata->nshutdown_gpio); -- cgit v1.2.3-59-g8ed1b From f049c545465de225d0ceb036b2dd08c5cfd31344 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Fri, 26 Jun 2020 14:05:19 +0100 Subject: misc: lkdtm: bugs: At least try to use popuated variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The result may not be intereresting, but not using a set variable is bad form and causes W=1 kernel builds to complain. Fixes the following W=1 warning(s): drivers/misc/lkdtm/bugs.c: In function ‘lkdtm_STACK_GUARD_PAGE_LEADING’: drivers/misc/lkdtm/bugs.c:331:25: warning: variable ‘byte’ set but not used [-Wunused-but-set-variable] 331 | volatile unsigned char byte; | ^~~~ drivers/misc/lkdtm/bugs.c: In function ‘lkdtm_STACK_GUARD_PAGE_TRAILING’: drivers/misc/lkdtm/bugs.c:345:25: warning: variable ‘byte’ set but not used [-Wunused-but-set-variable] 345 | volatile unsigned char byte; | ^~~~ Cc: Kees Cook Acked-by: Kees Cook Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20200626130525.389469-5-lee.jones@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/lkdtm/bugs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/misc/lkdtm/bugs.c b/drivers/misc/lkdtm/bugs.c index 10338800f6be..4dfbfd51bdf7 100644 --- a/drivers/misc/lkdtm/bugs.c +++ b/drivers/misc/lkdtm/bugs.c @@ -333,7 +333,7 @@ void lkdtm_STACK_GUARD_PAGE_LEADING(void) byte = *ptr; - pr_err("FAIL: accessed page before stack!\n"); + pr_err("FAIL: accessed page before stack! (byte: %x)\n", byte); } /* Test that VMAP_STACK is actually allocating with a trailing guard page */ @@ -347,7 +347,7 @@ void lkdtm_STACK_GUARD_PAGE_TRAILING(void) byte = *ptr; - pr_err("FAIL: accessed page after stack!\n"); + pr_err("FAIL: accessed page after stack! (byte: %x)\n", byte); } void lkdtm_UNSET_SMEP(void) -- cgit v1.2.3-59-g8ed1b From 98e72eb6434baf960dbf090312411b8ebc4f64d0 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Fri, 26 Jun 2020 14:05:21 +0100 Subject: misc: eeprom: eeprom_93cx6: Repair function arg descriptions Copy-paste issue. Looks like the kerneldoc style descriptions for these functions were taken from existing functions with slightly different argument names. Fixes the following W=1 warnings: drivers/misc/eeprom/eeprom_93cx6.c:239: warning: Function parameter or member 'byte' not described in 'eeprom_93cx6_readb' drivers/misc/eeprom/eeprom_93cx6.c:239: warning: Excess function parameter 'word' description in 'eeprom_93cx6_readb' drivers/misc/eeprom/eeprom_93cx6.c:280: warning: Function parameter or member 'bytes' not described in 'eeprom_93cx6_multireadb' drivers/misc/eeprom/eeprom_93cx6.c:280: warning: Excess function parameter 'words' description in 'eeprom_93cx6_multireadb' Cc: Wolfram Sang Acked-by: Arnd Bergmann Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20200626130525.389469-7-lee.jones@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/eeprom/eeprom_93cx6.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/misc/eeprom/eeprom_93cx6.c b/drivers/misc/eeprom/eeprom_93cx6.c index 36a2eb837371..9627294fe3e9 100644 --- a/drivers/misc/eeprom/eeprom_93cx6.c +++ b/drivers/misc/eeprom/eeprom_93cx6.c @@ -228,7 +228,7 @@ EXPORT_SYMBOL_GPL(eeprom_93cx6_multiread); /** * eeprom_93cx6_readb - Read a byte from eeprom * @eeprom: Pointer to eeprom structure - * @word: Byte index from where we should start reading + * @byte: Byte index from where we should start reading * @data: target pointer where the information will have to be stored * * This function will read a byte of the eeprom data @@ -270,7 +270,7 @@ EXPORT_SYMBOL_GPL(eeprom_93cx6_readb); * @eeprom: Pointer to eeprom structure * @byte: Index from where we should start reading * @data: target pointer where the information will have to be stored - * @words: Number of bytes that should be read. + * @bytes: Number of bytes that should be read. * * This function will read all requested bytes from the eeprom, * this is done by calling eeprom_93cx6_readb() multiple times. -- cgit v1.2.3-59-g8ed1b From e4be4884f3a120091b0fc0d7aa30660da8dac6a9 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Fri, 26 Jun 2020 14:05:22 +0100 Subject: misc: mic: vop: vop_main: Remove set but unused variable 'ret' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Hasn't been checked since its conception 2 years ago. Squashes W=1 warning: drivers/misc/mic/vop/vop_main.c: In function ‘_vop_scan_devices’: drivers/misc/mic/vop/vop_main.c:617:6: warning: variable ‘ret’ set but not used [-Wunused-but-set-variable] 617 | int ret; | ^~~ Cc: Sudeep Dutt Cc: Ashutosh Dixit Cc: Christian Borntraeger Acked-by: Arnd Bergmann Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20200626130525.389469-8-lee.jones@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/vop/vop_main.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/misc/mic/vop/vop_main.c b/drivers/misc/mic/vop/vop_main.c index 51a1ab83a630..7ce66890edad 100644 --- a/drivers/misc/mic/vop/vop_main.c +++ b/drivers/misc/mic/vop/vop_main.c @@ -614,7 +614,6 @@ static void _vop_scan_devices(void __iomem *dp, struct vop_device *vpdev, struct mic_device_desc __iomem *d; struct mic_device_ctrl __iomem *dc; struct device *dev; - int ret; for (i = sizeof(struct mic_bootparam); i < MIC_DP_SIZE; i += _vop_total_desc_size(d)) { @@ -644,7 +643,7 @@ static void _vop_scan_devices(void __iomem *dp, struct vop_device *vpdev, &dc->config_change); put_device(dev); _vop_handle_config_change(d, i, vpdev); - ret = _vop_remove_device(d, i, vpdev); + _vop_remove_device(d, i, vpdev); if (remove) { iowrite8(0, &dc->config_change); iowrite8(0, &dc->guest_ack); -- cgit v1.2.3-59-g8ed1b From dfe40cccac190da8076d436290ec3efc21169e68 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Fri, 26 Jun 2020 14:05:23 +0100 Subject: misc: cb710: sgbuf2: Add missing documentation for cb710_sg_dwiter_write_next_block()'s 'data' arg MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit An attempt was made to provide a proper kerneldoc header for cb710_sg_dwiter_write_next_block(), but a description for it's 'data' argument was missed. Squashes W=1 kernel build warning: drivers/misc/cb710/sgbuf2.c:131: warning: Function parameter or member 'data' not described in 'cb710_sg_dwiter_write_next_block' Cc: "Michał Mirosław" Acked-by: Arnd Bergmann Acked-by: Michał Mirosław Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20200626130525.389469-9-lee.jones@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/cb710/sgbuf2.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/misc/cb710/sgbuf2.c b/drivers/misc/cb710/sgbuf2.c index dfd2969e3628..e5a4ed3701eb 100644 --- a/drivers/misc/cb710/sgbuf2.c +++ b/drivers/misc/cb710/sgbuf2.c @@ -117,6 +117,7 @@ static void sg_dwiter_write_slow(struct sg_mapping_iter *miter, uint32_t data) /** * cb710_sg_dwiter_write_next_block() - write next 32-bit word to sg buffer * @miter: sg mapping iterator used for writing + * @data: data to write to sg buffer * * Description: * Writes 32-bit word starting at byte pointed to by @miter@ -- cgit v1.2.3-59-g8ed1b From 9eea2a499fa86b0822d27f3cd34c4554c6a9c2e6 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Fri, 26 Jun 2020 14:05:24 +0100 Subject: misc: habanalabs: irq: Add missing struct identifier for 'struct hl_eqe_work' In kerneldoc format, data structures have to start with 'struct' else the kerneldoc tooling/parsers/validators get confused. Squashes the following W=1 warning: drivers/misc/habanalabs/irq.c:19: warning: cannot understand function prototype: 'struct hl_eqe_work ' Cc: Oded Gabbay Reviewed-by: Oded Gabbay Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20200626130525.389469-10-lee.jones@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/habanalabs/irq.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/misc/habanalabs/irq.c b/drivers/misc/habanalabs/irq.c index fac65fbd70e8..4e77a7385779 100644 --- a/drivers/misc/habanalabs/irq.c +++ b/drivers/misc/habanalabs/irq.c @@ -10,7 +10,8 @@ #include /** - * This structure is used to schedule work of EQ entry and armcp_reset event + * struct hl_eqe_work - This structure is used to schedule work of EQ + * entry and armcp_reset event * * @eq_work - workqueue object to run when EQ entry is received * @hdev - pointer to device structure -- cgit v1.2.3-59-g8ed1b From ba2104c24aba1fa7e19d53f08c985526a6786d8b Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Fri, 26 Jun 2020 14:05:25 +0100 Subject: misc: pti: Fix documentation for bit-rotted function pti_tty_driver_write() The API has moved on since the original function header was authored. This changes brings the function's documentation back into line with reality, complete descriptions of the latest arguments to be used. Squashes the following W=1 kernel build warnings: drivers/misc/pti.c:510: warning: Function parameter or member 'tty' not described in 'pti_tty_driver_wr drivers/misc/pti.c:510: warning: Function parameter or member 'buf' not described in 'pti_tty_driver_wr drivers/misc/pti.c:510: warning: Excess function parameter 'filp' description in 'pti_tty_driver_write' drivers/misc/pti.c:510: warning: Excess function parameter 'data' description in 'pti_tty_driver_write' Cc: J Freyensee Acked-by: Arnd Bergmann Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20200626130525.389469-11-lee.jones@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/pti.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/misc/pti.c b/drivers/misc/pti.c index b7f510676cd6..07e9da7918eb 100644 --- a/drivers/misc/pti.c +++ b/drivers/misc/pti.c @@ -496,9 +496,8 @@ static void pti_tty_cleanup(struct tty_struct *tty) * pti_tty_driver_write()- Write trace debugging data through the char * interface to the PTI HW. Part of the misc device implementation. * - * @filp: Contains private data which is used to obtain - * master, channel write ID. - * @data: trace data to be written. + * @tty: tty struct containing pti information. + * @buf: trace data to be written. * @len: # of byte to write. * * Returns: -- cgit v1.2.3-59-g8ed1b From e3f88cdb8fdd5876ef3a0373e35ba7f2598fcf17 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 9 Jun 2020 04:54:33 +0800 Subject: soundwire: add definitions for 1.2 spec Add definitions for register offsets and bit fields from the MIPI SoundWire 1.2 specification (available to MIPI members at https://members.mipi.org/wg/All-Members/document/download/78371) Signed-off-by: Pierre-Louis Bossart Reviewed-by: Kai Vehmanen Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20200608205436.2402-2-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- include/linux/soundwire/sdw_registers.h | 107 +++++++++++++++++++++++++++++++- 1 file changed, 106 insertions(+), 1 deletion(-) diff --git a/include/linux/soundwire/sdw_registers.h b/include/linux/soundwire/sdw_registers.h index a686f7988156..12f9ffc3eb3b 100644 --- a/include/linux/soundwire/sdw_registers.h +++ b/include/linux/soundwire/sdw_registers.h @@ -12,7 +12,7 @@ #define SDW_REG_SHIFT(n) (ffs(n) - 1) /* - * SDW registers as defined by MIPI 1.1 Spec + * SDW registers as defined by MIPI 1.2 Spec */ #define SDW_REGADDR GENMASK(14, 0) #define SDW_SCP_ADDRPAGE2_MASK GENMASK(22, 15) @@ -43,6 +43,8 @@ #define SDW_DP0_INT_TEST_FAIL BIT(0) #define SDW_DP0_INT_PORT_READY BIT(1) #define SDW_DP0_INT_BRA_FAILURE BIT(2) +#define SDW_DP0_SDCA_CASCADE BIT(3) +/* BIT(4) not allocated in SoundWire specification 1.2 */ #define SDW_DP0_INT_IMPDEF1 BIT(5) #define SDW_DP0_INT_IMPDEF2 BIT(6) #define SDW_DP0_INT_IMPDEF3 BIT(7) @@ -106,6 +108,10 @@ #define SDW_SCP_ADDRPAGE2 0x49 #define SDW_SCP_KEEPEREN 0x4A #define SDW_SCP_BANKDELAY 0x4B +#define SDW_SCP_COMMIT 0x4C +#define SDW_SCP_BUS_CLOCK_BASE 0x4D +#define SDW_SCP_BASE_CLOCK_FREQ GENMASK(2, 0) +/* 0x4E is not allocated in SoundWire specification 1.2 */ #define SDW_SCP_TESTMODE 0x4F #define SDW_SCP_DEVID_0 0x50 #define SDW_SCP_DEVID_1 0x51 @@ -114,12 +120,111 @@ #define SDW_SCP_DEVID_4 0x54 #define SDW_SCP_DEVID_5 0x55 +/* Both INT and STATUS register are same */ +#define SDW_SCP_SDCA_INT1 0x58 +#define SDW_SCP_SDCA_INT_SDCA_0 BIT(0) +#define SDW_SCP_SDCA_INT_SDCA_1 BIT(1) +#define SDW_SCP_SDCA_INT_SDCA_2 BIT(2) +#define SDW_SCP_SDCA_INT_SDCA_3 BIT(3) +#define SDW_SCP_SDCA_INT_SDCA_4 BIT(4) +#define SDW_SCP_SDCA_INT_SDCA_5 BIT(5) +#define SDW_SCP_SDCA_INT_SDCA_6 BIT(6) +#define SDW_SCP_SDCA_INT_SDCA_7 BIT(7) + +#define SDW_SCP_SDCA_INT2 0x59 +#define SDW_SCP_SDCA_INT_SDCA_8 BIT(0) +#define SDW_SCP_SDCA_INT_SDCA_9 BIT(1) +#define SDW_SCP_SDCA_INT_SDCA_10 BIT(2) +#define SDW_SCP_SDCA_INT_SDCA_11 BIT(3) +#define SDW_SCP_SDCA_INT_SDCA_12 BIT(4) +#define SDW_SCP_SDCA_INT_SDCA_13 BIT(5) +#define SDW_SCP_SDCA_INT_SDCA_14 BIT(6) +#define SDW_SCP_SDCA_INT_SDCA_15 BIT(7) + +#define SDW_SCP_SDCA_INT3 0x5A +#define SDW_SCP_SDCA_INT_SDCA_16 BIT(0) +#define SDW_SCP_SDCA_INT_SDCA_17 BIT(1) +#define SDW_SCP_SDCA_INT_SDCA_18 BIT(2) +#define SDW_SCP_SDCA_INT_SDCA_19 BIT(3) +#define SDW_SCP_SDCA_INT_SDCA_20 BIT(4) +#define SDW_SCP_SDCA_INT_SDCA_21 BIT(5) +#define SDW_SCP_SDCA_INT_SDCA_22 BIT(6) +#define SDW_SCP_SDCA_INT_SDCA_23 BIT(7) + +#define SDW_SCP_SDCA_INT4 0x5B +#define SDW_SCP_SDCA_INT_SDCA_24 BIT(0) +#define SDW_SCP_SDCA_INT_SDCA_25 BIT(1) +#define SDW_SCP_SDCA_INT_SDCA_26 BIT(2) +#define SDW_SCP_SDCA_INT_SDCA_27 BIT(3) +#define SDW_SCP_SDCA_INT_SDCA_28 BIT(4) +#define SDW_SCP_SDCA_INT_SDCA_29 BIT(5) +#define SDW_SCP_SDCA_INT_SDCA_30 BIT(6) +/* BIT(7) not allocated in SoundWire 1.2 specification */ + +#define SDW_SCP_SDCA_INTMASK1 0x5C +#define SDW_SCP_SDCA_INTMASK_SDCA_0 BIT(0) +#define SDW_SCP_SDCA_INTMASK_SDCA_1 BIT(1) +#define SDW_SCP_SDCA_INTMASK_SDCA_2 BIT(2) +#define SDW_SCP_SDCA_INTMASK_SDCA_3 BIT(3) +#define SDW_SCP_SDCA_INTMASK_SDCA_4 BIT(4) +#define SDW_SCP_SDCA_INTMASK_SDCA_5 BIT(5) +#define SDW_SCP_SDCA_INTMASK_SDCA_6 BIT(6) +#define SDW_SCP_SDCA_INTMASK_SDCA_7 BIT(7) + +#define SDW_SCP_SDCA_INTMASK2 0x5D +#define SDW_SCP_SDCA_INTMASK_SDCA_8 BIT(0) +#define SDW_SCP_SDCA_INTMASK_SDCA_9 BIT(1) +#define SDW_SCP_SDCA_INTMASK_SDCA_10 BIT(2) +#define SDW_SCP_SDCA_INTMASK_SDCA_11 BIT(3) +#define SDW_SCP_SDCA_INTMASK_SDCA_12 BIT(4) +#define SDW_SCP_SDCA_INTMASK_SDCA_13 BIT(5) +#define SDW_SCP_SDCA_INTMASK_SDCA_14 BIT(6) +#define SDW_SCP_SDCA_INTMASK_SDCA_15 BIT(7) + +#define SDW_SCP_SDCA_INTMASK3 0x5E +#define SDW_SCP_SDCA_INTMASK_SDCA_16 BIT(0) +#define SDW_SCP_SDCA_INTMASK_SDCA_17 BIT(1) +#define SDW_SCP_SDCA_INTMASK_SDCA_18 BIT(2) +#define SDW_SCP_SDCA_INTMASK_SDCA_19 BIT(3) +#define SDW_SCP_SDCA_INTMASK_SDCA_20 BIT(4) +#define SDW_SCP_SDCA_INTMASK_SDCA_21 BIT(5) +#define SDW_SCP_SDCA_INTMASK_SDCA_22 BIT(6) +#define SDW_SCP_SDCA_INTMASK_SDCA_23 BIT(7) + +#define SDW_SCP_SDCA_INTMASK4 0x5F +#define SDW_SCP_SDCA_INTMASK_SDCA_24 BIT(0) +#define SDW_SCP_SDCA_INTMASK_SDCA_25 BIT(1) +#define SDW_SCP_SDCA_INTMASK_SDCA_26 BIT(2) +#define SDW_SCP_SDCA_INTMASK_SDCA_27 BIT(3) +#define SDW_SCP_SDCA_INTMASK_SDCA_28 BIT(4) +#define SDW_SCP_SDCA_INTMASK_SDCA_29 BIT(5) +#define SDW_SCP_SDCA_INTMASK_SDCA_30 BIT(6) +/* BIT(7) not allocated in SoundWire 1.2 specification */ + /* Banked Registers */ #define SDW_SCP_FRAMECTRL_B0 0x60 #define SDW_SCP_FRAMECTRL_B1 (0x60 + SDW_BANK1_OFFSET) #define SDW_SCP_NEXTFRAME_B0 0x61 #define SDW_SCP_NEXTFRAME_B1 (0x61 + SDW_BANK1_OFFSET) +#define SDW_SCP_BUSCLOCK_SCALE_B0 0x62 +#define SDW_SCP_BUSCLOCK_SCALE_B1 (0x62 + SDW_BANK1_OFFSET) +#define SDW_SCP_CLOCK_SCALE GENMASK(3, 0) + +/* PHY registers - CTRL and STAT are the same address */ +#define SDW_SCP_PHY_OUT_CTRL_0 0x80 +#define SDW_SCP_PHY_OUT_CTRL_1 0x81 +#define SDW_SCP_PHY_OUT_CTRL_2 0x82 +#define SDW_SCP_PHY_OUT_CTRL_3 0x83 +#define SDW_SCP_PHY_OUT_CTRL_4 0x84 +#define SDW_SCP_PHY_OUT_CTRL_5 0x85 +#define SDW_SCP_PHY_OUT_CTRL_6 0x86 +#define SDW_SCP_PHY_OUT_CTRL_7 0x87 + +#define SDW_SCP_CAP_LOAD_CTRL GENMASK(2, 0) +#define SDW_SCP_DRIVE_STRENGTH_CTRL GENMASK(5, 3) +#define SDW_SCP_SLEW_TIME_CTRL GENMASK(7, 6) + /* Both INT and STATUS register is same */ #define SDW_DPN_INT(n) (0x0 + SDW_DPN_SIZE * (n)) #define SDW_DPN_INTMASK(n) (0x1 + SDW_DPN_SIZE * (n)) -- cgit v1.2.3-59-g8ed1b From ee9173db5037cf46aba47161043ec6f0e06cf9d3 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 9 Jun 2020 04:54:34 +0800 Subject: soundwire: bus_type: convert open-coded while() to for() loop No functionality change, just more structured code. Suggested-by: Guennadi Liakhovetski Signed-off-by: Pierre-Louis Bossart Reviewed-by: Rander Wang Reviewed-by: Guennadi Liakhovetski Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20200608205436.2402-3-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/bus_type.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/soundwire/bus_type.c b/drivers/soundwire/bus_type.c index de9a671802b8..c8d948c09d9d 100644 --- a/drivers/soundwire/bus_type.c +++ b/drivers/soundwire/bus_type.c @@ -20,14 +20,12 @@ static const struct sdw_device_id * sdw_get_device_id(struct sdw_slave *slave, struct sdw_driver *drv) { - const struct sdw_device_id *id = drv->id_table; + const struct sdw_device_id *id; - while (id && id->mfg_id) { + for (id = drv->id_table; id && id->mfg_id; id++) if (slave->id.mfg_id == id->mfg_id && slave->id.part_id == id->part_id) return id; - id++; - } return NULL; } -- cgit v1.2.3-59-g8ed1b From b5924268d6700821fbd8afe815073b05cebaa308 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 9 Jun 2020 04:54:35 +0800 Subject: soundwire: extend SDW_SLAVE_ENTRY The SoundWire 1.2 specification adds new capabilities that were not present in previous version, such as the class ID. To enable support for class drivers, and well as drivers that address a specific version, all fields of the sdw_device_id structure need to be exposed. For SoundWire 1.0 and 1.1 devices, a wildcard is used so class and version information are ignored. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Rander Wang Reviewed-by: Guennadi Liakhovetski Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20200608205436.2402-4-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/bus_type.c | 13 +++++++++---- include/linux/mod_devicetable.h | 2 ++ include/linux/soundwire/sdw.h | 11 +++++++---- scripts/mod/devicetable-offsets.c | 2 ++ scripts/mod/file2alias.c | 6 +++++- 5 files changed, 25 insertions(+), 9 deletions(-) diff --git a/drivers/soundwire/bus_type.c b/drivers/soundwire/bus_type.c index c8d948c09d9d..6fba55898cf0 100644 --- a/drivers/soundwire/bus_type.c +++ b/drivers/soundwire/bus_type.c @@ -24,7 +24,11 @@ sdw_get_device_id(struct sdw_slave *slave, struct sdw_driver *drv) for (id = drv->id_table; id && id->mfg_id; id++) if (slave->id.mfg_id == id->mfg_id && - slave->id.part_id == id->part_id) + slave->id.part_id == id->part_id && + (!id->sdw_version || + slave->id.sdw_version == id->sdw_version) && + (!id->class_id || + slave->id.class_id == id->class_id)) return id; return NULL; @@ -47,10 +51,11 @@ static int sdw_bus_match(struct device *dev, struct device_driver *ddrv) int sdw_slave_modalias(const struct sdw_slave *slave, char *buf, size_t size) { - /* modalias is sdw:mp */ + /* modalias is sdw:mpvc */ - return snprintf(buf, size, "sdw:m%04Xp%04X\n", - slave->id.mfg_id, slave->id.part_id); + return snprintf(buf, size, "sdw:m%04Xp%04Xv%02Xc%02X\n", + slave->id.mfg_id, slave->id.part_id, + slave->id.sdw_version, slave->id.class_id); } int sdw_slave_uevent(struct device *dev, struct kobj_uevent_env *env) diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index 8d764aab29de..f8585e3a2c43 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -251,6 +251,8 @@ struct hda_device_id { struct sdw_device_id { __u16 mfg_id; __u16 part_id; + __u8 sdw_version; + __u8 class_id; kernel_ulong_t driver_data; }; diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index 9c27a32df9bb..64c9314cb903 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -426,8 +426,7 @@ int sdw_slave_read_prop(struct sdw_slave *slave); * struct sdw_slave_id - Slave ID * @mfg_id: MIPI Manufacturer ID * @part_id: Device Part ID - * @class_id: MIPI Class ID, unused now. - * Currently a placeholder in MIPI SoundWire Spec + * @class_id: MIPI Class ID (defined starting with SoundWire 1.2 spec) * @unique_id: Device unique ID * @sdw_version: SDW version implemented * @@ -659,10 +658,14 @@ struct sdw_driver { struct device_driver driver; }; -#define SDW_SLAVE_ENTRY(_mfg_id, _part_id, _drv_data) \ - { .mfg_id = (_mfg_id), .part_id = (_part_id), \ +#define SDW_SLAVE_ENTRY_EXT(_mfg_id, _part_id, _version, _c_id, _drv_data) \ + { .mfg_id = (_mfg_id), .part_id = (_part_id), \ + .sdw_version = (_version), .class_id = (_c_id), \ .driver_data = (unsigned long)(_drv_data) } +#define SDW_SLAVE_ENTRY(_mfg_id, _part_id, _drv_data) \ + SDW_SLAVE_ENTRY_EXT((_mfg_id), (_part_id), 0, 0, (_drv_data)) + int sdw_handle_slave_status(struct sdw_bus *bus, enum sdw_slave_status status[]); diff --git a/scripts/mod/devicetable-offsets.c b/scripts/mod/devicetable-offsets.c index 010be8ba2116..27007c18e754 100644 --- a/scripts/mod/devicetable-offsets.c +++ b/scripts/mod/devicetable-offsets.c @@ -216,6 +216,8 @@ int main(void) DEVID(sdw_device_id); DEVID_FIELD(sdw_device_id, mfg_id); DEVID_FIELD(sdw_device_id, part_id); + DEVID_FIELD(sdw_device_id, sdw_version); + DEVID_FIELD(sdw_device_id, class_id); DEVID(fsl_mc_device_id); DEVID_FIELD(fsl_mc_device_id, vendor); diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c index 9599e2a3f1e6..2417dd1dee33 100644 --- a/scripts/mod/file2alias.c +++ b/scripts/mod/file2alias.c @@ -1258,15 +1258,19 @@ static int do_hda_entry(const char *filename, void *symval, char *alias) return 1; } -/* Looks like: sdw:mNpN */ +/* Looks like: sdw:mNpNvNcN */ static int do_sdw_entry(const char *filename, void *symval, char *alias) { DEF_FIELD(symval, sdw_device_id, mfg_id); DEF_FIELD(symval, sdw_device_id, part_id); + DEF_FIELD(symval, sdw_device_id, sdw_version); + DEF_FIELD(symval, sdw_device_id, class_id); strcpy(alias, "sdw:"); ADD(alias, "m", mfg_id != 0, mfg_id); ADD(alias, "p", part_id != 0, part_id); + ADD(alias, "v", sdw_version != 0, sdw_version); + ADD(alias, "c", class_id != 0, class_id); add_wildcard(alias); return 1; -- cgit v1.2.3-59-g8ed1b From 29d158f906907ce8e52ea75ec87b4e35461f2018 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 9 Jun 2020 04:54:36 +0800 Subject: soundwire: bus: initialize bus clock base and scale registers The SoundWire 1.2 specification adds new registers to allow for seamless clock changes while audio transfers are on-going. Program them following the specification. Note that dynamic clock changes are not supported for now, this only adds the register initialization. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Kai Vehmanen Reviewed-by: Guennadi Liakhovetski Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20200608205436.2402-5-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/bus.c | 107 ++++++++++++++++++++++++++++++++ include/linux/soundwire/sdw_registers.h | 10 +++ 2 files changed, 117 insertions(+) diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index fcfba1c38267..e6e0fb9a81b4 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -1070,12 +1070,119 @@ int sdw_configure_dpn_intr(struct sdw_slave *slave, return ret; } +static int sdw_slave_set_frequency(struct sdw_slave *slave) +{ + u32 mclk_freq = slave->bus->prop.mclk_freq; + u32 curr_freq = slave->bus->params.curr_dr_freq >> 1; + unsigned int scale; + u8 scale_index; + u8 base; + int ret; + + /* + * frequency base and scale registers are required for SDCA + * devices. They may also be used for 1.2+/non-SDCA devices, + * but we will need a DisCo property to cover this case + */ + if (!slave->id.class_id) + return 0; + + if (!mclk_freq) { + dev_err(&slave->dev, + "no bus MCLK, cannot set SDW_SCP_BUS_CLOCK_BASE\n"); + return -EINVAL; + } + + /* + * map base frequency using Table 89 of SoundWire 1.2 spec. + * The order of the tests just follows the specification, this + * is not a selection between possible values or a search for + * the best value but just a mapping. Only one case per platform + * is relevant. + * Some BIOS have inconsistent values for mclk_freq but a + * correct root so we force the mclk_freq to avoid variations. + */ + if (!(19200000 % mclk_freq)) { + mclk_freq = 19200000; + base = SDW_SCP_BASE_CLOCK_19200000_HZ; + } else if (!(24000000 % mclk_freq)) { + mclk_freq = 24000000; + base = SDW_SCP_BASE_CLOCK_24000000_HZ; + } else if (!(24576000 % mclk_freq)) { + mclk_freq = 24576000; + base = SDW_SCP_BASE_CLOCK_24576000_HZ; + } else if (!(22579200 % mclk_freq)) { + mclk_freq = 22579200; + base = SDW_SCP_BASE_CLOCK_22579200_HZ; + } else if (!(32000000 % mclk_freq)) { + mclk_freq = 32000000; + base = SDW_SCP_BASE_CLOCK_32000000_HZ; + } else { + dev_err(&slave->dev, + "Unsupported clock base, mclk %d\n", + mclk_freq); + return -EINVAL; + } + + if (mclk_freq % curr_freq) { + dev_err(&slave->dev, + "mclk %d is not multiple of bus curr_freq %d\n", + mclk_freq, curr_freq); + return -EINVAL; + } + + scale = mclk_freq / curr_freq; + + /* + * map scale to Table 90 of SoundWire 1.2 spec - and check + * that the scale is a power of two and maximum 64 + */ + scale_index = ilog2(scale); + + if (BIT(scale_index) != scale || scale_index > 6) { + dev_err(&slave->dev, + "No match found for scale %d, bus mclk %d curr_freq %d\n", + scale, mclk_freq, curr_freq); + return -EINVAL; + } + scale_index++; + + ret = sdw_write(slave, SDW_SCP_BUS_CLOCK_BASE, base); + if (ret < 0) { + dev_err(&slave->dev, + "SDW_SCP_BUS_CLOCK_BASE write failed:%d\n", ret); + return ret; + } + + /* initialize scale for both banks */ + ret = sdw_write(slave, SDW_SCP_BUSCLOCK_SCALE_B0, scale_index); + if (ret < 0) { + dev_err(&slave->dev, + "SDW_SCP_BUSCLOCK_SCALE_B0 write failed:%d\n", ret); + return ret; + } + ret = sdw_write(slave, SDW_SCP_BUSCLOCK_SCALE_B1, scale_index); + if (ret < 0) + dev_err(&slave->dev, + "SDW_SCP_BUSCLOCK_SCALE_B1 write failed:%d\n", ret); + + dev_dbg(&slave->dev, + "Configured bus base %d, scale %d, mclk %d, curr_freq %d\n", + base, scale_index, mclk_freq, curr_freq); + + return ret; +} + static int sdw_initialize_slave(struct sdw_slave *slave) { struct sdw_slave_prop *prop = &slave->prop; int ret; u8 val; + ret = sdw_slave_set_frequency(slave); + if (ret < 0) + return ret; + /* * Set bus clash, parity and SCP implementation * defined interrupt mask diff --git a/include/linux/soundwire/sdw_registers.h b/include/linux/soundwire/sdw_registers.h index 12f9ffc3eb3b..5d3c271af7d1 100644 --- a/include/linux/soundwire/sdw_registers.h +++ b/include/linux/soundwire/sdw_registers.h @@ -109,8 +109,18 @@ #define SDW_SCP_KEEPEREN 0x4A #define SDW_SCP_BANKDELAY 0x4B #define SDW_SCP_COMMIT 0x4C + #define SDW_SCP_BUS_CLOCK_BASE 0x4D #define SDW_SCP_BASE_CLOCK_FREQ GENMASK(2, 0) +#define SDW_SCP_BASE_CLOCK_UNKNOWN 0x0 +#define SDW_SCP_BASE_CLOCK_19200000_HZ 0x1 +#define SDW_SCP_BASE_CLOCK_24000000_HZ 0x2 +#define SDW_SCP_BASE_CLOCK_24576000_HZ 0x3 +#define SDW_SCP_BASE_CLOCK_22579200_HZ 0x4 +#define SDW_SCP_BASE_CLOCK_32000000_HZ 0x5 +#define SDW_SCP_BASE_CLOCK_RESERVED 0x6 +#define SDW_SCP_BASE_CLOCK_IMP_DEF 0x7 + /* 0x4E is not allocated in SoundWire specification 1.2 */ #define SDW_SCP_TESTMODE 0x4F #define SDW_SCP_DEVID_0 0x50 -- cgit v1.2.3-59-g8ed1b From 81148a7ab79e8a5f1e9379d5fcdd7fb73896ec18 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 1 Jul 2020 13:44:25 +0200 Subject: Revert "cardreader/rtsx_pcr.c: use generic power management" This reverts commit 6bf23661d4a7a105001ebb4410c1c0a17752fac4. 0-day reported build problems with it. Reported-by: kernel test robot Cc: Vaibhav Gupta Signed-off-by: Greg Kroah-Hartman --- drivers/misc/cardreader/rtsx_pcr.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/drivers/misc/cardreader/rtsx_pcr.c b/drivers/misc/cardreader/rtsx_pcr.c index ca5212cba1c0..0d5928bc1b6d 100644 --- a/drivers/misc/cardreader/rtsx_pcr.c +++ b/drivers/misc/cardreader/rtsx_pcr.c @@ -1604,9 +1604,10 @@ static void rtsx_pci_remove(struct pci_dev *pcidev) pci_name(pcidev), (int)pcidev->vendor, (int)pcidev->device); } -static int __maybe_unused rtsx_pci_suspend(struct device *dev_d) +#ifdef CONFIG_PM + +static int rtsx_pci_suspend(struct pci_dev *pcidev, pm_message_t state) { - struct pci_dev *pcidev = to_pci_dev(dev_d); struct pcr_handle *handle; struct rtsx_pcr *pcr; @@ -1622,15 +1623,17 @@ static int __maybe_unused rtsx_pci_suspend(struct device *dev_d) rtsx_pci_power_off(pcr, HOST_ENTER_S3); - device_wakeup_disable(dev_d); + pci_save_state(pcidev); + pci_enable_wake(pcidev, pci_choose_state(pcidev, state), 0); + pci_disable_device(pcidev); + pci_set_power_state(pcidev, pci_choose_state(pcidev, state)); mutex_unlock(&pcr->pcr_mutex); return 0; } -static int __maybe_unused rtsx_pci_resume(struct device *dev_d) +static int rtsx_pci_resume(struct pci_dev *pcidev) { - struct pci_dev *pcidev = to_pci_dev(dev_d); struct pcr_handle *handle; struct rtsx_pcr *pcr; int ret = 0; @@ -1642,6 +1645,11 @@ static int __maybe_unused rtsx_pci_resume(struct device *dev_d) mutex_lock(&pcr->pcr_mutex); + pci_set_power_state(pcidev, PCI_D0); + pci_restore_state(pcidev); + ret = pci_enable_device(pcidev); + if (ret) + goto out; pci_set_master(pcidev); ret = rtsx_pci_write_register(pcr, HOST_SLEEP_STATE, 0x03, 0x00); @@ -1659,8 +1667,6 @@ out: return ret; } -#ifdef CONFIG_PM - static void rtsx_pci_shutdown(struct pci_dev *pcidev) { struct pcr_handle *handle; @@ -1680,18 +1686,19 @@ static void rtsx_pci_shutdown(struct pci_dev *pcidev) #else /* CONFIG_PM */ +#define rtsx_pci_suspend NULL +#define rtsx_pci_resume NULL #define rtsx_pci_shutdown NULL #endif /* CONFIG_PM */ -static SIMPLE_DEV_PM_OPS(rtsx_pci_pm_ops, rtsx_pci_suspend, rtsx_pci_resume); - static struct pci_driver rtsx_pci_driver = { .name = DRV_NAME_RTSX_PCI, .id_table = rtsx_pci_ids, .probe = rtsx_pci_probe, .remove = rtsx_pci_remove, - .driver.pm = &rtsx_pci_pm_ops, + .suspend = rtsx_pci_suspend, + .resume = rtsx_pci_resume, .shutdown = rtsx_pci_shutdown, }; module_pci_driver(rtsx_pci_driver); -- cgit v1.2.3-59-g8ed1b From f2e1d306fc7dc0f813bcd3559dc6959272a0aaa9 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 1 Jul 2020 09:58:34 +0100 Subject: misc: pti: Repair kerneldoc formatting issues W=1 kernel builds report a lack of descriptions for various function arguments. In reality they are documented, but the formatting was not as expected '@.*:'. Instead, '-'s were used as separators. This change fixes the following warnings: drivers/misc/pti.c:748: warning: Function parameter or member 'port' not described in 'pti_port_activate' drivers/misc/pti.c:748: warning: Function parameter or member 'tty' not described in 'pti_port_activate' drivers/misc/pti.c:765: warning: Function parameter or member 'port' not described in 'pti_port_shutdown' drivers/misc/pti.c:793: warning: Function parameter or member 'pdev' not described in 'pti_pci_probe' drivers/misc/pti.c:793: warning: Function parameter or member 'ent' not described in 'pti_pci_probe' Cc: J Freyensee Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20200701085853.164358-2-lee.jones@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/pti.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/misc/pti.c b/drivers/misc/pti.c index 07e9da7918eb..e19988766aa6 100644 --- a/drivers/misc/pti.c +++ b/drivers/misc/pti.c @@ -733,8 +733,8 @@ static struct console pti_console = { * pti_port_activate()- Used to start/initialize any items upon * first opening of tty_port(). * - * @port- The tty port number of the PTI device. - * @tty- The tty struct associated with this device. + * @port: The tty port number of the PTI device. + * @tty: The tty struct associated with this device. * * Returns: * always returns 0 @@ -754,7 +754,7 @@ static int pti_port_activate(struct tty_port *port, struct tty_struct *tty) * pti_port_shutdown()- Used to stop/shutdown any items upon the * last tty port close. * - * @port- The tty port number of the PTI device. + * @port: The tty port number of the PTI device. * * Notes: The primary purpose of the PTI tty port 0 is to hook * the syslog daemon to it; thus this port will be open for a @@ -780,8 +780,8 @@ static const struct tty_port_operations tty_port_ops = { * pti_pci_probe()- Used to detect pti on the pci bus and set * things up in the driver. * - * @pdev- pci_dev struct values for pti. - * @ent- pci_device_id struct for pti driver. + * @pdev: pci_dev struct values for pti. + * @ent: pci_device_id struct for pti driver. * * Returns: * 0 for success -- cgit v1.2.3-59-g8ed1b From 6e47fc8a68230c8803db2724b8a22f2725753386 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 1 Jul 2020 09:58:35 +0100 Subject: misc: pti: Remove unparsable empty line in function header The kerneldoc tooling/parsers/validators get confused if non- standard formatting is used. The first line after the kerneldoc identifier '/**' must not be blank else the following warnings will be issued: drivers/misc/pti.c:902: warning: Cannot understand * on line 902 - I thought it was a doc line Cc: J Freyensee Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20200701085853.164358-3-lee.jones@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/pti.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/misc/pti.c b/drivers/misc/pti.c index e19988766aa6..7236ae527b19 100644 --- a/drivers/misc/pti.c +++ b/drivers/misc/pti.c @@ -898,7 +898,6 @@ static struct pci_driver pti_pci_driver = { }; /** - * * pti_init()- Overall entry/init call to the pti driver. * It starts the registration process with the kernel. * -- cgit v1.2.3-59-g8ed1b From a0c11b3c91e053128d13d988de8b9bcb76c5adfe Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 1 Jul 2020 09:58:36 +0100 Subject: misc: habanalabs: firmware_if: Add missing 'fw_name' and 'dst' entries to function header Looks as though documentation for these function arguments have been missing since the driver's inception last year. Fixes the following W=1 kernel build warnings: drivers/misc/habanalabs/firmware_if.c:26: warning: Function parameter or member 'fw_name' not described in 'hl_fw_load_fw_to_device' drivers/misc/habanalabs/firmware_if.c:26: warning: Function parameter or member 'dst' not described in 'hl_fw_load_fw_to_device' Cc: Oded Gabbay Cc: Tomer Tayar Signed-off-by: Lee Jones Reviewed-by: Oded Gabbay Link: https://lore.kernel.org/r/20200701085853.164358-4-lee.jones@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/habanalabs/firmware_if.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/misc/habanalabs/firmware_if.c b/drivers/misc/habanalabs/firmware_if.c index baf790cf4b78..15e0793da655 100644 --- a/drivers/misc/habanalabs/firmware_if.c +++ b/drivers/misc/habanalabs/firmware_if.c @@ -15,7 +15,10 @@ /** * hl_fw_load_fw_to_device() - Load F/W code to device's memory. + * * @hdev: pointer to hl_device structure. + * @fw_name: the firmware image name + * @dst: IO memory mapped address space to copy firmware to * * Copy fw code from firmware file to device memory. * -- cgit v1.2.3-59-g8ed1b From df123c9dcde7f14a69a276b801b310805d200ada Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 1 Jul 2020 09:58:37 +0100 Subject: misc: habanalabs: pci: Fix a variety of kerneldoc issues hl_pci_bars_map() has a miss-typed argument name in the function description. hl_pci_elbi_write() was missing documented arguments. The headers for functions hl_pci_bars_unmap(), hl_pci_elbi_write() and hl_pci_reset_link_through_bridge() were written in kerneldoc format, but lack the kerneldoc identifier '/**'. Let's promote them so they can gain access to the checker. These changes fix the following W=1 kernel build warnings: drivers/misc/habanalabs/pci.c:27: warning: Function parameter or member 'name' not described in 'hl_pci_bars_map' drivers/misc/habanalabs/pci.c:27: warning: Excess function parameter 'bar_name' description in 'hl_pci_bars_map' drivers/misc/habanalabs/pci.c:147: warning: Function parameter or member 'addr' not described in 'hl_pci_iatu_write' drivers/misc/habanalabs/pci.c:147: warning: Function parameter or member 'data' not described in 'hl_pci_iatu_write' drivers/misc/habanalabs/pci.c:324: warning: Excess function parameter 'dma_mask' description in 'hl_pci_set_dma_mask' Cc: Oded Gabbay Cc: Tomer Tayar Signed-off-by: Lee Jones Reviewed-by: Oded Gabbay Link: https://lore.kernel.org/r/20200701085853.164358-5-lee.jones@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/habanalabs/pci.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/misc/habanalabs/pci.c b/drivers/misc/habanalabs/pci.c index 9f634ef6f5b3..24073e8eb8df 100644 --- a/drivers/misc/habanalabs/pci.c +++ b/drivers/misc/habanalabs/pci.c @@ -15,7 +15,7 @@ /** * hl_pci_bars_map() - Map PCI BARs. * @hdev: Pointer to hl_device structure. - * @bar_name: Array of BAR names. + * @name: Array of BAR names. * @is_wc: Array with flag per BAR whether a write-combined mapping is needed. * * Request PCI regions and map them to kernel virtual addresses. @@ -61,7 +61,7 @@ err: return rc; } -/* +/** * hl_pci_bars_unmap() - Unmap PCI BARS. * @hdev: Pointer to hl_device structure. * @@ -80,9 +80,11 @@ static void hl_pci_bars_unmap(struct hl_device *hdev) pci_release_regions(pdev); } -/* +/** * hl_pci_elbi_write() - Write through the ELBI interface. * @hdev: Pointer to hl_device structure. + * @addr: Address to write to + * @data: Data to write * * Return: 0 on success, negative value for failure. */ @@ -140,6 +142,8 @@ static int hl_pci_elbi_write(struct hl_device *hdev, u64 addr, u32 data) /** * hl_pci_iatu_write() - iatu write routine. * @hdev: Pointer to hl_device structure. + * @addr: Address to write to + * @data: Data to write * * Return: 0 on success, negative value for failure. */ @@ -161,7 +165,7 @@ int hl_pci_iatu_write(struct hl_device *hdev, u32 addr, u32 data) return 0; } -/* +/** * hl_pci_reset_link_through_bridge() - Reset PCI link. * @hdev: Pointer to hl_device structure. */ -- cgit v1.2.3-59-g8ed1b From 3db99f000b76bbd5d3ffc042e4cf91e934c996ac Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 1 Jul 2020 09:58:38 +0100 Subject: misc: habanalabs: irq: Repair kerneldoc formatting issues W=1 kernel builds report a lack of descriptions for various function arguments. In reality they are documented, but the formatting was not as expected '@.*:'. Instead, '-'s were used as separators. While we're here, the headers for functions various functions were written in kerneldoc format, but lack the kerneldoc identifier '/**'. Let's promote them so they can gain access to the checker. This change fixes the following W=1 warnings: drivers/misc/habanalabs/irq.c:24: warning: Function parameter or member 'eq_work' not described in 'hl_eqe_work' drivers/misc/habanalabs/irq.c:24: warning: Function parameter or member 'hdev' not described in 'hl_eqe_work' drivers/misc/habanalabs/irq.c:24: warning: Function parameter or member 'eq_entry' not described in 'hl_eqe_work' Cc: Oded Gabbay Cc: Tomer Tayar Signed-off-by: Lee Jones Reviewed-by: Oded Gabbay Link: https://lore.kernel.org/r/20200701085853.164358-6-lee.jones@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/habanalabs/irq.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/misc/habanalabs/irq.c b/drivers/misc/habanalabs/irq.c index 4e77a7385779..6981d67153b1 100644 --- a/drivers/misc/habanalabs/irq.c +++ b/drivers/misc/habanalabs/irq.c @@ -13,9 +13,9 @@ * struct hl_eqe_work - This structure is used to schedule work of EQ * entry and armcp_reset event * - * @eq_work - workqueue object to run when EQ entry is received - * @hdev - pointer to device structure - * @eq_entry - copy of the EQ entry + * @eq_work: workqueue object to run when EQ entry is received + * @hdev: pointer to device structure + * @eq_entry: copy of the EQ entry */ struct hl_eqe_work { struct work_struct eq_work; @@ -23,7 +23,7 @@ struct hl_eqe_work { struct hl_eq_entry eq_entry; }; -/* +/** * hl_cq_inc_ptr - increment ci or pi of cq * * @ptr: the current ci or pi value of the completion queue @@ -39,7 +39,7 @@ inline u32 hl_cq_inc_ptr(u32 ptr) return ptr; } -/* +/** * hl_eq_inc_ptr - increment ci of eq * * @ptr: the current ci value of the event queue @@ -66,7 +66,7 @@ static void irq_handle_eqe(struct work_struct *work) kfree(eqe_work); } -/* +/** * hl_irq_handler_cq - irq handler for completion queue * * @irq: irq number @@ -142,7 +142,7 @@ irqreturn_t hl_irq_handler_cq(int irq, void *arg) return IRQ_HANDLED; } -/* +/** * hl_irq_handler_eq - irq handler for event queue * * @irq: irq number @@ -206,7 +206,7 @@ skip_irq: return IRQ_HANDLED; } -/* +/** * hl_cq_init - main initialization function for an cq object * * @hdev: pointer to device structure @@ -238,7 +238,7 @@ int hl_cq_init(struct hl_device *hdev, struct hl_cq *q, u32 hw_queue_id) return 0; } -/* +/** * hl_cq_fini - destroy completion queue * * @hdev: pointer to device structure @@ -269,7 +269,7 @@ void hl_cq_reset(struct hl_device *hdev, struct hl_cq *q) memset((void *) (uintptr_t) q->kernel_address, 0, HL_CQ_SIZE_IN_BYTES); } -/* +/** * hl_eq_init - main initialization function for an event queue object * * @hdev: pointer to device structure @@ -297,7 +297,7 @@ int hl_eq_init(struct hl_device *hdev, struct hl_eq *q) return 0; } -/* +/** * hl_eq_fini - destroy event queue * * @hdev: pointer to device structure -- cgit v1.2.3-59-g8ed1b From 2557f27fd4891c3475b764d6ce3c48cc9265d424 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 1 Jul 2020 09:58:39 +0100 Subject: misc: habanalabs: goya: Omit pointless check ensuring addr is >=0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Seeing as 'addr' is unsigned, it would be impossible for the assigned value to be anything other than zero or positive. Squashes the following W=1 warnings: drivers/misc/habanalabs/goya/goya.c: In function ‘goya_debugfs_read32’: drivers/misc/habanalabs/goya/goya.c:3945:19: warning: comparison of unsigned expression >= 0 is always true [-Wtype-limits] 3945 | } else if ((addr >= DRAM_PHYS_BASE) && | ^~ drivers/misc/habanalabs/goya/goya.c: In function ‘goya_debugfs_write32’: drivers/misc/habanalabs/goya/goya.c:4002:19: warning: comparison of unsigned expression >= 0 is always true [-Wtype-limits] 4002 | } else if ((addr >= DRAM_PHYS_BASE) && | ^~ drivers/misc/habanalabs/goya/goya.c: In function ‘goya_debugfs_read64’: drivers/misc/habanalabs/goya/goya.c:4047:19: warning: comparison of unsigned expression >= 0 is always true [-Wtype-limits] 4047 | } else if ((addr >= DRAM_PHYS_BASE) && | ^~ drivers/misc/habanalabs/goya/goya.c: In function ‘goya_debugfs_write64’: drivers/misc/habanalabs/goya/goya.c:4091:19: warning: comparison of unsigned expression >= 0 is always true [-Wtype-limits] 4091 | } else if ((addr >= DRAM_PHYS_BASE) && | ^~ drivers/misc/habanalabs/pci.c:328: warning: Excess function parameter 'dma_mask' description in 'hl_pci_set_dma_mask' drivers/misc/habanalabs/goya/goya_coresight.c: In function ‘goya_debug_coresight’: drivers/misc/habanalabs/goya/goya_coresight.c:643:6: warning: variable ‘val’ set but not used [-Wunused-but-set-variable] 643 | u32 val; | ^~~ Cc: Oded Gabbay Cc: Tomer Tayar Signed-off-by: Lee Jones Reviewed-by: Oded Gabbay Link: https://lore.kernel.org/r/20200701085853.164358-7-lee.jones@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/habanalabs/goya/goya.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/drivers/misc/habanalabs/goya/goya.c b/drivers/misc/habanalabs/goya/goya.c index 0d2952bb58df..a4a20e27ed3b 100644 --- a/drivers/misc/habanalabs/goya/goya.c +++ b/drivers/misc/habanalabs/goya/goya.c @@ -3942,8 +3942,7 @@ static int goya_debugfs_read32(struct hl_device *hdev, u64 addr, u32 *val) *val = readl(hdev->pcie_bar[SRAM_CFG_BAR_ID] + (addr - SRAM_BASE_ADDR)); - } else if ((addr >= DRAM_PHYS_BASE) && - (addr < DRAM_PHYS_BASE + hdev->asic_prop.dram_size)) { + } else if (addr < DRAM_PHYS_BASE + hdev->asic_prop.dram_size) { u64 bar_base_addr = DRAM_PHYS_BASE + (addr & ~(prop->dram_pci_bar_size - 0x1ull)); @@ -3999,8 +3998,7 @@ static int goya_debugfs_write32(struct hl_device *hdev, u64 addr, u32 val) writel(val, hdev->pcie_bar[SRAM_CFG_BAR_ID] + (addr - SRAM_BASE_ADDR)); - } else if ((addr >= DRAM_PHYS_BASE) && - (addr < DRAM_PHYS_BASE + hdev->asic_prop.dram_size)) { + } else if (addr < DRAM_PHYS_BASE + hdev->asic_prop.dram_size) { u64 bar_base_addr = DRAM_PHYS_BASE + (addr & ~(prop->dram_pci_bar_size - 0x1ull)); @@ -4044,9 +4042,8 @@ static int goya_debugfs_read64(struct hl_device *hdev, u64 addr, u64 *val) *val = readq(hdev->pcie_bar[SRAM_CFG_BAR_ID] + (addr - SRAM_BASE_ADDR)); - } else if ((addr >= DRAM_PHYS_BASE) && - (addr <= - DRAM_PHYS_BASE + hdev->asic_prop.dram_size - sizeof(u64))) { + } else if (addr <= + DRAM_PHYS_BASE + hdev->asic_prop.dram_size - sizeof(u64)) { u64 bar_base_addr = DRAM_PHYS_BASE + (addr & ~(prop->dram_pci_bar_size - 0x1ull)); @@ -4088,9 +4085,8 @@ static int goya_debugfs_write64(struct hl_device *hdev, u64 addr, u64 val) writeq(val, hdev->pcie_bar[SRAM_CFG_BAR_ID] + (addr - SRAM_BASE_ADDR)); - } else if ((addr >= DRAM_PHYS_BASE) && - (addr <= - DRAM_PHYS_BASE + hdev->asic_prop.dram_size - sizeof(u64))) { + } else if (addr <= + DRAM_PHYS_BASE + hdev->asic_prop.dram_size - sizeof(u64)) { u64 bar_base_addr = DRAM_PHYS_BASE + (addr & ~(prop->dram_pci_bar_size - 0x1ull)); -- cgit v1.2.3-59-g8ed1b From e0712c600e7b3e7cf29de9dc0cd178aac5250958 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 1 Jul 2020 09:58:40 +0100 Subject: misc: habanalabs: pci: Scrub documentation for non-present function argument 'dma_mask' is not passed directly into hl_pci_set_dma_mask() as an argument. Instead, it is pulled from struct hl_device *hdev. Fixed the following W=1 warning: drivers/misc/habanalabs/pci.c:328: warning: Excess function parameter 'dma_mask' description in 'hl_pci_set_dma_mask Cc: Oded Gabbay Cc: Tomer Tayar Signed-off-by: Lee Jones Reviewed-by: Oded Gabbay Link: https://lore.kernel.org/r/20200701085853.164358-8-lee.jones@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/habanalabs/pci.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/misc/habanalabs/pci.c b/drivers/misc/habanalabs/pci.c index 24073e8eb8df..61a8bb07262c 100644 --- a/drivers/misc/habanalabs/pci.c +++ b/drivers/misc/habanalabs/pci.c @@ -317,7 +317,6 @@ int hl_pci_init_iatu(struct hl_device *hdev, u64 sram_base_address, /** * hl_pci_set_dma_mask() - Set DMA masks for the device. * @hdev: Pointer to hl_device structure. - * @dma_mask: number of bits for the requested dma mask. * * This function sets the DMA masks (regular and consistent) for a specified * value. If it doesn't succeed, it tries to set it to a fall-back value -- cgit v1.2.3-59-g8ed1b From 67db05cea6f66da68fda47a0e38422c3d19479a8 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 1 Jul 2020 09:58:41 +0100 Subject: misc: habanalabs: goya: goya_coresight: Remove set but unused variable 'val' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No attempt to check the return value of RREG32() has been made since the call was introduced a year ago. Fixes W=1 kernel build warning: drivers/misc/habanalabs/goya/goya_coresight.c: In function ‘goya_debug_coresight’: drivers/misc/habanalabs/goya/goya_coresight.c:643:6: warning: variable ‘val’ set but not used [-Wunused-but-set-variable] 643 | u32 val; | ^~~ Cc: Oded Gabbay Cc: Tomer Tayar Signed-off-by: Lee Jones Reviewed-by: Oded Gabbay Link: https://lore.kernel.org/r/20200701085853.164358-9-lee.jones@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/habanalabs/goya/goya_coresight.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/misc/habanalabs/goya/goya_coresight.c b/drivers/misc/habanalabs/goya/goya_coresight.c index 1258724ea510..aa51fc71f0a1 100644 --- a/drivers/misc/habanalabs/goya/goya_coresight.c +++ b/drivers/misc/habanalabs/goya/goya_coresight.c @@ -640,7 +640,6 @@ static int goya_config_spmu(struct hl_device *hdev, int goya_debug_coresight(struct hl_device *hdev, void *data) { struct hl_debug_params *params = data; - u32 val; int rc = 0; switch (params->op) { @@ -672,7 +671,7 @@ int goya_debug_coresight(struct hl_device *hdev, void *data) } /* Perform read from the device to flush all configuration */ - val = RREG32(mmPCIE_DBI_DEVICE_ID_VENDOR_ID_REG); + RREG32(mmPCIE_DBI_DEVICE_ID_VENDOR_ID_REG); return rc; } -- cgit v1.2.3-59-g8ed1b From f7d227c306531bca8def3e6e13be977a52554d85 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 1 Jul 2020 09:58:42 +0100 Subject: misc: habanalabs: gaudi: Remove ill placed asterisk from kerneldoc header W=1 kernel builds report a lack of description of gaudi_set_asic_funcs()'s 'hdev' argument. In reality it is documented, but the formatting was not as expected '@.*:'. Instead, there was a misplaced asterisk which was confusing the kerneldoc validator. Squashes the following W=1 warning: drivers/misc/habanalabs/gaudi/gaudi.c:6746: warning: Function parameter or member 'hdev' not described in 'gaudi_set_asic_funcs' Cc: Oded Gabbay Cc: Tomer Tayar Signed-off-by: Lee Jones Reviewed-by: Oded Gabbay Link: https://lore.kernel.org/r/20200701085853.164358-10-lee.jones@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/habanalabs/gaudi/gaudi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/habanalabs/gaudi/gaudi.c b/drivers/misc/habanalabs/gaudi/gaudi.c index 834470d10b46..7dd99ec41627 100644 --- a/drivers/misc/habanalabs/gaudi/gaudi.c +++ b/drivers/misc/habanalabs/gaudi/gaudi.c @@ -6772,7 +6772,7 @@ static const struct hl_asic_funcs gaudi_funcs = { /** * gaudi_set_asic_funcs - set GAUDI function pointers * - * @*hdev: pointer to hl_device structure + * @hdev: pointer to hl_device structure * */ void gaudi_set_asic_funcs(struct hl_device *hdev) -- cgit v1.2.3-59-g8ed1b From 14395c6fb1350d59bf8fed1f5a224c638af77196 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 1 Jul 2020 09:58:43 +0100 Subject: misc: habanalabs: gaudi: gaudi_security: Repair incorrectly named function arg gaudi_pb_set_block()'s argument 'base' was incorrectly named 'block' in its function header. Fixes the following W=1 kernel build warning(s): drivers/misc/habanalabs/gaudi/gaudi_security.c:454: warning: Function parameter or member 'base' not described in 'gaudi_pb_set_block' drivers/misc/habanalabs/gaudi/gaudi_security.c:454: warning: Excess function parameter 'block' description in 'gaudi_pb_set_block' Cc: Oded Gabbay Cc: Tomer Tayar Signed-off-by: Lee Jones Reviewed-by: Oded Gabbay Link: https://lore.kernel.org/r/20200701085853.164358-11-lee.jones@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/habanalabs/gaudi/gaudi_security.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/misc/habanalabs/gaudi/gaudi_security.c b/drivers/misc/habanalabs/gaudi/gaudi_security.c index 6a351e31fa6a..abdd5ed8f2cf 100644 --- a/drivers/misc/habanalabs/gaudi/gaudi_security.c +++ b/drivers/misc/habanalabs/gaudi/gaudi_security.c @@ -447,8 +447,7 @@ static u64 gaudi_rr_hbw_mask_high_ar_regs[GAUDI_NUMBER_OF_RR_REGS] = { * gaudi_set_block_as_protected - set the given block as protected * * @hdev: pointer to hl_device structure - * @block: block base address - * + * @base: block base address */ static void gaudi_pb_set_block(struct hl_device *hdev, u64 base) { -- cgit v1.2.3-59-g8ed1b From 82f5b473d91a7f4e687207fb6d53f7c55669fcd0 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 1 Jul 2020 09:58:44 +0100 Subject: misc: enclosure: Fix some kerneldoc anomalies Firstly some missing function argument documentation, then some whch are present, but are incorrectly named. Fixes the following W=1 kernel build warnings: drivers/misc/enclosure.c:115: warning: Function parameter or member 'name' not described in 'enclosure_register' drivers/misc/enclosure.c:115: warning: Function parameter or member 'cb' not described in 'enclosure_register' drivers/misc/enclosure.c:283: warning: Function parameter or member 'number' not described in 'enclosure_component_alloc' drivers/misc/enclosure.c:283: warning: Excess function parameter 'num' description in 'enclosure_component_alloc' drivers/misc/enclosure.c:363: warning: Function parameter or member 'component' not described in 'enclosure_add_device' drivers/misc/enclosure.c:363: warning: Excess function parameter 'num' description in 'enclosure_add_device' drivers/misc/enclosure.c:398: warning: Function parameter or member 'dev' not described in 'enclosure_remove_device' drivers/misc/enclosure.c:398: warning: Excess function parameter 'num' description in 'enclosure_remove_device' Cc: James Bottomley Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20200701085853.164358-12-lee.jones@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/enclosure.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/misc/enclosure.c b/drivers/misc/enclosure.c index 3c2d405bc79b..e8eba52750b3 100644 --- a/drivers/misc/enclosure.c +++ b/drivers/misc/enclosure.c @@ -103,7 +103,9 @@ EXPORT_SYMBOL_GPL(enclosure_for_each_device); * enclosure_register - register device as an enclosure * * @dev: device containing the enclosure + * @name: chosen device name * @components: number of components in the enclosure + * @cb: platform call-backs * * This sets up the device for being an enclosure. Note that @dev does * not have to be a dedicated enclosure device. It may be some other type @@ -266,7 +268,7 @@ static const struct attribute_group *enclosure_component_groups[]; /** * enclosure_component_alloc - prepare a new enclosure component * @edev: the enclosure to add the component - * @num: the device number + * @number: the device number * @type: the type of component being added * @name: an optional name to appear in sysfs (leave NULL if none) * @@ -347,7 +349,7 @@ EXPORT_SYMBOL_GPL(enclosure_component_register); /** * enclosure_add_device - add a device as being part of an enclosure * @edev: the enclosure device being added to. - * @num: the number of the component + * @component: the number of the component * @dev: the device being added * * Declares a real device to reside in slot (or identifier) @num of an -- cgit v1.2.3-59-g8ed1b From 8bc056e84a66e37efda30a3a66db9a27f023d9ba Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 1 Jul 2020 09:58:45 +0100 Subject: misc: lattice-ecp3-config: Remove set but clearly unused variable 'ret' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It's odd for the return value to be assigned to a variable so many times, but never actually checked, but this has been the case since the driver's inception in 2012. If it hasn't caused any issues by now, it's probably unlikely to. Let's take it out, at least until someone finds a reason to start using it. Fixes the following W=1 kernel build warning: drivers/misc/lattice-ecp3-config.c: In function ‘firmware_load’: drivers/misc/lattice-ecp3-config.c:70:6: warning: variable ‘ret’ set but not used [-Wunused-but-set-variable] 70 | int ret; | ^~~ Cc: Stefan Roese Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20200701085853.164358-13-lee.jones@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/lattice-ecp3-config.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/drivers/misc/lattice-ecp3-config.c b/drivers/misc/lattice-ecp3-config.c index 884485c3f723..5eaf74447ca1 100644 --- a/drivers/misc/lattice-ecp3-config.c +++ b/drivers/misc/lattice-ecp3-config.c @@ -67,7 +67,6 @@ static void firmware_load(const struct firmware *fw, void *context) struct spi_device *spi = (struct spi_device *)context; struct fpga_data *data = spi_get_drvdata(spi); u8 *buffer; - int ret; u8 txbuf[8]; u8 rxbuf[8]; int rx_len = 8; @@ -92,7 +91,7 @@ static void firmware_load(const struct firmware *fw, void *context) /* Trying to speak with the FPGA via SPI... */ txbuf[0] = FPGA_CMD_READ_ID; - ret = spi_write_then_read(spi, txbuf, 8, rxbuf, rx_len); + spi_write_then_read(spi, txbuf, 8, rxbuf, rx_len); jedec_id = get_unaligned_be32(&rxbuf[4]); dev_dbg(&spi->dev, "FPGA JTAG ID=%08x\n", jedec_id); @@ -110,7 +109,7 @@ static void firmware_load(const struct firmware *fw, void *context) dev_info(&spi->dev, "FPGA %s detected\n", ecp3_dev[i].name); txbuf[0] = FPGA_CMD_READ_STATUS; - ret = spi_write_then_read(spi, txbuf, 8, rxbuf, rx_len); + spi_write_then_read(spi, txbuf, 8, rxbuf, rx_len); status = get_unaligned_be32(&rxbuf[4]); dev_dbg(&spi->dev, "FPGA Status=%08x\n", status); @@ -130,20 +129,20 @@ static void firmware_load(const struct firmware *fw, void *context) memcpy(buffer + 4, fw->data, fw->size); txbuf[0] = FPGA_CMD_REFRESH; - ret = spi_write(spi, txbuf, 4); + spi_write(spi, txbuf, 4); txbuf[0] = FPGA_CMD_WRITE_EN; - ret = spi_write(spi, txbuf, 4); + spi_write(spi, txbuf, 4); txbuf[0] = FPGA_CMD_CLEAR; - ret = spi_write(spi, txbuf, 4); + spi_write(spi, txbuf, 4); /* * Wait for FPGA memory to become cleared */ for (i = 0; i < FPGA_CLEAR_LOOP_COUNT; i++) { txbuf[0] = FPGA_CMD_READ_STATUS; - ret = spi_write_then_read(spi, txbuf, 8, rxbuf, rx_len); + spi_write_then_read(spi, txbuf, 8, rxbuf, rx_len); status = get_unaligned_be32(&rxbuf[4]); if (status == FPGA_STATUS_CLEARED) break; @@ -160,13 +159,13 @@ static void firmware_load(const struct firmware *fw, void *context) } dev_info(&spi->dev, "Configuring the FPGA...\n"); - ret = spi_write(spi, buffer, fw->size + 8); + spi_write(spi, buffer, fw->size + 8); txbuf[0] = FPGA_CMD_WRITE_DIS; - ret = spi_write(spi, txbuf, 4); + spi_write(spi, txbuf, 4); txbuf[0] = FPGA_CMD_READ_STATUS; - ret = spi_write_then_read(spi, txbuf, 8, rxbuf, rx_len); + spi_write_then_read(spi, txbuf, 8, rxbuf, rx_len); status = get_unaligned_be32(&rxbuf[4]); dev_dbg(&spi->dev, "FPGA Status=%08x\n", status); -- cgit v1.2.3-59-g8ed1b From 0f8ab9bd9e48c90728f4b80672fc58009fa2b071 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 1 Jul 2020 09:58:46 +0100 Subject: misc: pch_phub: Provide descriptions for 'chip' argument For some reason (probably copy 'n' paste) kerneldoc descriptions were missing for all instances of 'chip'. Providing them squashes the following W=1 kernel build warnings: drivers/misc/pch_phub.c:145: warning: Function parameter or member 'chip' not described in 'pch_phub_read_modify_write_reg' drivers/misc/pch_phub.c:282: warning: Function parameter or member 'chip' not described in 'pch_phub_read_serial_rom' drivers/misc/pch_phub.c:296: warning: Function parameter or member 'chip' not described in 'pch_phub_write_serial_rom' drivers/misc/pch_phub.c:334: warning: Function parameter or member 'chip' not described in 'pch_phub_read_serial_rom_val' drivers/misc/pch_phub.c:350: warning: Function parameter or member 'chip' not described in 'pch_phub_write_serial_rom_val' drivers/misc/pch_phub.c:450: warning: Function parameter or member 'chip' not described in 'pch_phub_read_gbe_mac_addr' drivers/misc/pch_phub.c:462: warning: Function parameter or member 'chip' not described in 'pch_phub_write_gbe_mac_addr' Cc: Masayuki Ohtak Cc: Tomoya MORINAGA Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20200701085853.164358-14-lee.jones@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/pch_phub.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/misc/pch_phub.c b/drivers/misc/pch_phub.c index 9321d4239932..6fe24e5ec98f 100644 --- a/drivers/misc/pch_phub.c +++ b/drivers/misc/pch_phub.c @@ -135,6 +135,7 @@ static DEFINE_MUTEX(pch_phub_mutex); /** * pch_phub_read_modify_write_reg() - Reading modifying and writing register + * @chip: Pointer to the PHUB register structure * @reg_addr_offset: Register offset address value. * @data: Writing value. * @mask: Mask value. @@ -272,6 +273,7 @@ static void __maybe_unused pch_phub_restore_reg_conf(struct pci_dev *pdev) /** * pch_phub_read_serial_rom() - Reading Serial ROM + * @chip: Pointer to the PHUB register structure * @offset_address: Serial ROM offset address to read. * @data: Read buffer for specified Serial ROM value. */ @@ -286,6 +288,7 @@ static void pch_phub_read_serial_rom(struct pch_phub_reg *chip, /** * pch_phub_write_serial_rom() - Writing Serial ROM + * @chip: Pointer to the PHUB register structure * @offset_address: Serial ROM offset address. * @data: Serial ROM value to write. */ @@ -324,6 +327,7 @@ static int pch_phub_write_serial_rom(struct pch_phub_reg *chip, /** * pch_phub_read_serial_rom_val() - Read Serial ROM value + * @chip: Pointer to the PHUB register structure * @offset_address: Serial ROM address offset value. * @data: Serial ROM value to read. */ @@ -340,6 +344,7 @@ static void pch_phub_read_serial_rom_val(struct pch_phub_reg *chip, /** * pch_phub_write_serial_rom_val() - writing Serial ROM value + * @chip: Pointer to the PHUB register structure * @offset_address: Serial ROM address offset value. * @data: Serial ROM value. */ @@ -442,6 +447,7 @@ static int pch_phub_gbe_serial_rom_conf_mp(struct pch_phub_reg *chip) /** * pch_phub_read_gbe_mac_addr() - Read Gigabit Ethernet MAC address * @offset_address: Gigabit Ethernet MAC address offset value. + * @chip: Pointer to the PHUB register structure * @data: Buffer of the Gigabit Ethernet MAC address value. */ static void pch_phub_read_gbe_mac_addr(struct pch_phub_reg *chip, u8 *data) @@ -454,6 +460,7 @@ static void pch_phub_read_gbe_mac_addr(struct pch_phub_reg *chip, u8 *data) /** * pch_phub_write_gbe_mac_addr() - Write MAC address * @offset_address: Gigabit Ethernet MAC address offset value. + * @chip: Pointer to the PHUB register structure * @data: Gigabit Ethernet MAC address value. */ static int pch_phub_write_gbe_mac_addr(struct pch_phub_reg *chip, u8 *data) -- cgit v1.2.3-59-g8ed1b From 7b411871220af35722360cd1d1111c7fadfc29fe Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 1 Jul 2020 09:58:47 +0100 Subject: misc: pch_phub: Remove superfluous descriptions to non-existent args 'offset_address' Probably a copy 'n' paste error, 'offset_address' has never been part of the pch_phub_{read,write}_gbe_mac_addr() functions. Squashes the following W=1 warnings: drivers/misc/pch_phub.c:450: warning: Excess function parameter 'offset_address' description in 'pch_phub_read_gbe_mac_addr' drivers/misc/pch_phub.c:462: warning: Excess function parameter 'offset_address' description in 'pch_phub_write_gbe_mac_addr' Cc: Masayuki Ohtak Cc: Tomoya MORINAGA Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20200701085853.164358-15-lee.jones@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/pch_phub.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/misc/pch_phub.c b/drivers/misc/pch_phub.c index 6fe24e5ec98f..8d2b7135738e 100644 --- a/drivers/misc/pch_phub.c +++ b/drivers/misc/pch_phub.c @@ -446,7 +446,6 @@ static int pch_phub_gbe_serial_rom_conf_mp(struct pch_phub_reg *chip) /** * pch_phub_read_gbe_mac_addr() - Read Gigabit Ethernet MAC address - * @offset_address: Gigabit Ethernet MAC address offset value. * @chip: Pointer to the PHUB register structure * @data: Buffer of the Gigabit Ethernet MAC address value. */ @@ -459,7 +458,6 @@ static void pch_phub_read_gbe_mac_addr(struct pch_phub_reg *chip, u8 *data) /** * pch_phub_write_gbe_mac_addr() - Write MAC address - * @offset_address: Gigabit Ethernet MAC address offset value. * @chip: Pointer to the PHUB register structure * @data: Gigabit Ethernet MAC address value. */ -- cgit v1.2.3-59-g8ed1b From 6a57251c70a4ab48ed5efa8a55f43d0f6b47d508 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 1 Jul 2020 09:58:48 +0100 Subject: misc: enclosure: Update enclosure_remove_device() documentation to match reality enclosure_remove_device() hasn't taken an 'int component for over a decade. Instead use kerneldoc to describe the 'struct device' actually passed in. Fixes the following W=1 kernel build warning(s): drivers/misc/enclosure.c:400: warning: Function parameter or member 'dev' not described in 'enclosure_remove_device' drivers/misc/enclosure.c:400: warning: Excess function parameter 'num' description in 'enclosure_remove_device' Cc: James Bottomley Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20200701085853.164358-16-lee.jones@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/enclosure.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/enclosure.c b/drivers/misc/enclosure.c index e8eba52750b3..f950d0155876 100644 --- a/drivers/misc/enclosure.c +++ b/drivers/misc/enclosure.c @@ -391,7 +391,7 @@ EXPORT_SYMBOL_GPL(enclosure_add_device); /** * enclosure_remove_device - remove a device from an enclosure * @edev: the enclosure device - * @num: the number of the component to remove + * @dev: device to remove/put * * Returns zero on success or an error. * -- cgit v1.2.3-59-g8ed1b From f33e92e50c82319718a21ec372f3df9eff59bfa9 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 1 Jul 2020 09:58:49 +0100 Subject: misc: genwqe: card_base: Remove set but unused variable 'rc' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Variable 'rc' hasn't been checked since the driver's inception in 2013. If it hasn't caused any issues since then, it's unlikely to in the future. Let's take it out for now. Fixes the following W=1 kernel build warning(s): drivers/misc/genwqe/card_base.c: In function ‘genwqe_health_check_stop’: /home/lee/projects/linux/kernel/drivers/misc/genwqe/card_base.c:1046:6: warning: variable ‘rc’ set but not used [-Wunused-but-set-variable] 1046 | int rc; | ^~ Cc: Michael Jung Cc: Michael Ruettger Cc: Frank Haverkamp Cc: Joerg-Stephan Vogt Signed-off-by: Lee Jones Reviewed-by: Frank Haverkamp Link: https://lore.kernel.org/r/20200701085853.164358-17-lee.jones@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/genwqe/card_base.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/misc/genwqe/card_base.c b/drivers/misc/genwqe/card_base.c index 9969c0003f15..0e83192a8285 100644 --- a/drivers/misc/genwqe/card_base.c +++ b/drivers/misc/genwqe/card_base.c @@ -1043,12 +1043,10 @@ static int genwqe_health_thread_running(struct genwqe_dev *cd) static int genwqe_health_check_stop(struct genwqe_dev *cd) { - int rc; - if (!genwqe_health_thread_running(cd)) return -EIO; - rc = kthread_stop(cd->health_thread); + kthread_stop(cd->health_thread); cd->health_thread = NULL; return 0; } -- cgit v1.2.3-59-g8ed1b From f5721c7acc4af3cf1d0996f7a8b293a10cad05d4 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 1 Jul 2020 09:58:50 +0100 Subject: misc: genwqe: card_base: Provide documentation for genwqe_recover_card()'s args genwqe_recover_card()'s function arguments were missing from its kerneldoc header. Provide descriptions for 'cd' and 'fatal_err'. Fixes the following kernel build W=1 warning: drivers/misc/genwqe/card_base.c:588: warning: Function parameter or member 'cd' not described in 'genwqe_recover_card' drivers/misc/genwqe/card_base.c:588: warning: Function parameter or member 'fatal_err' not described in 'genwqe_recover_card' Signed-off-by: Lee Jones Reviewed-by: Frank Haverkamp Link: https://lore.kernel.org/r/20200701085853.164358-18-lee.jones@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/genwqe/card_base.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/misc/genwqe/card_base.c b/drivers/misc/genwqe/card_base.c index 0e83192a8285..eda15cc67adf 100644 --- a/drivers/misc/genwqe/card_base.c +++ b/drivers/misc/genwqe/card_base.c @@ -569,6 +569,8 @@ static int genwqe_stop(struct genwqe_dev *cd) /** * genwqe_recover_card() - Try to recover the card if it is possible + * @cd: GenWQE device information + * @fatal_err: Indicate whether to attempt soft reset * * If fatal_err is set no register access is possible anymore. It is * likely that genwqe_start fails in that situation. Proper error -- cgit v1.2.3-59-g8ed1b From a562c0c3e54a3878421e586fe3ea81a99bc02c84 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 1 Jul 2020 09:58:51 +0100 Subject: misc: genwqe: card_base: Whole host of kerneldoc fixes >From missing documentation for function arguments, to promotion obvious kerneldoc headers and incorrectly named arguments. Fixes the following W=1 warnings: drivers/misc/genwqe/card_base.c:175: warning: Function parameter or member 'cd' not described in 'genwqe_bus_reset' drivers/misc/genwqe/card_base.c:272: warning: Function parameter or member 'cd' not described in 'genwqe_recovery_on_fatal_gfir_required' drivers/misc/genwqe/card_base.c:293: warning: Function parameter or member 'cd' not described in 'genwqe_T_psec' drivers/misc/genwqe/card_base.c:314: warning: Function parameter or member 'cd' not described in 'genwqe_setup_pf_jtimer' drivers/misc/genwqe/card_base.c:334: warning: Function parameter or member 'cd' not described in 'genwqe_setup_vf_jtimer' drivers/misc/genwqe/card_base.c:557: warning: Function parameter or member 'cd' not described in 'genwqe_stop' drivers/misc/genwqe/card_base.c:617: warning: Function parameter or member 'cd' not described in 'genwqe_fir_checking' drivers/misc/genwqe/card_base.c:760: warning: Function parameter or member 'pci_dev' not described in 'genwqe_pci_fundamental_reset' drivers/misc/genwqe/card_base.c:889: warning: Function parameter or member 'data' not described in 'genwqe_health_thread' drivers/misc/genwqe/card_base.c:1046: warning: Function parameter or member 'cd' not described in 'genwqe_pci_setup' drivers/misc/genwqe/card_base.c:1131: warning: Function parameter or member 'cd' not described in 'genwqe_pci_remove' drivers/misc/genwqe/card_base.c:1151: warning: Function parameter or member 'pci_dev' not described in 'genwqe_probe' drivers/misc/genwqe/card_base.c:1151: warning: Function parameter or member 'id' not described in 'genwqe_probe' drivers/misc/genwqe/card_base.c:1151: warning: Excess function parameter 'pdev' description in 'genwqe_probe' drivers/misc/genwqe/card_base.c:1207: warning: Function parameter or member 'pci_dev' not described in 'genwqe_remove' drivers/misc/genwqe/card_base.c:1336: warning: Function parameter or member 'dev' not described in 'genwqe_devnode' drivers/misc/genwqe/card_base.c:1336: warning: Function parameter or member 'mode' not described in 'genwqe_devnode' Cc: Michael Jung Cc: Michael Ruettger Cc: Frank Haverkamp Cc: Joerg-Stephan Vogt Signed-off-by: Lee Jones Reviewed-by: Frank Haverkamp Link: https://lore.kernel.org/r/20200701085853.164358-19-lee.jones@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/genwqe/card_base.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/drivers/misc/genwqe/card_base.c b/drivers/misc/genwqe/card_base.c index eda15cc67adf..acc459fc8105 100644 --- a/drivers/misc/genwqe/card_base.c +++ b/drivers/misc/genwqe/card_base.c @@ -165,6 +165,7 @@ static void genwqe_dev_free(struct genwqe_dev *cd) /** * genwqe_bus_reset() - Card recovery + * @cd: GenWQE device information * * pci_reset_function() will recover the device and ensure that the * registers are accessible again when it completes with success. If @@ -262,6 +263,7 @@ static void genwqe_tweak_hardware(struct genwqe_dev *cd) /** * genwqe_recovery_on_fatal_gfir_required() - Version depended actions + * @cd: GenWQE device information * * Bitstreams older than 2013-02-17 have a bug where fatal GFIRs must * be ignored. This is e.g. true for the bitstream we gave to the card @@ -280,6 +282,7 @@ int genwqe_flash_readback_fails(struct genwqe_dev *cd) /** * genwqe_T_psec() - Calculate PF/VF timeout register content + * @cd: GenWQE device information * * Note: From a design perspective it turned out to be a bad idea to * use codes here to specifiy the frequency/speed values. An old @@ -303,6 +306,7 @@ static int genwqe_T_psec(struct genwqe_dev *cd) /** * genwqe_setup_pf_jtimer() - Setup PF hardware timeouts for DDCB execution + * @cd: GenWQE device information * * Do this _after_ card_reset() is called. Otherwise the values will * vanish. The settings need to be done when the queues are inactive. @@ -329,6 +333,7 @@ static bool genwqe_setup_pf_jtimer(struct genwqe_dev *cd) /** * genwqe_setup_vf_jtimer() - Setup VF hardware timeouts for DDCB execution + * @cd: GenWQE device information */ static bool genwqe_setup_vf_jtimer(struct genwqe_dev *cd) { @@ -543,6 +548,7 @@ static int genwqe_start(struct genwqe_dev *cd) /** * genwqe_stop() - Stop card operation + * @cd: GenWQE device information * * Recovery notes: * As long as genwqe_thread runs we might access registers during @@ -620,6 +626,7 @@ static int genwqe_health_check_cond(struct genwqe_dev *cd, u64 *gfir) /** * genwqe_fir_checking() - Check the fault isolation registers of the card + * @cd: GenWQE device information * * If this code works ok, can be tried out with help of the genwqe_poke tool: * sudo ./tools/genwqe_poke 0x8 0xfefefefefef @@ -764,6 +771,7 @@ static u64 genwqe_fir_checking(struct genwqe_dev *cd) /** * genwqe_pci_fundamental_reset() - trigger a PCIe fundamental reset on the slot + * @pci_dev: PCI device information struct * * Note: pci_set_pcie_reset_state() is not implemented on all archs, so this * reset method will not work in all cases. @@ -828,8 +836,9 @@ static int genwqe_platform_recovery(struct genwqe_dev *cd) return rc; } -/* +/** * genwqe_reload_bistream() - reload card bitstream + * @cd: GenWQE device information * * Set the appropriate register and call fundamental reset to reaload the card * bitstream. @@ -882,6 +891,7 @@ static int genwqe_reload_bistream(struct genwqe_dev *cd) /** * genwqe_health_thread() - Health checking thread + * @data: GenWQE device information * * This thread is only started for the PF of the card. * @@ -1055,6 +1065,7 @@ static int genwqe_health_check_stop(struct genwqe_dev *cd) /** * genwqe_pci_setup() - Allocate PCIe related resources for our card + * @cd: GenWQE device information */ static int genwqe_pci_setup(struct genwqe_dev *cd) { @@ -1140,6 +1151,7 @@ static int genwqe_pci_setup(struct genwqe_dev *cd) /** * genwqe_pci_remove() - Free PCIe related resources for our card + * @cd: GenWQE device information */ static void genwqe_pci_remove(struct genwqe_dev *cd) { @@ -1154,7 +1166,8 @@ static void genwqe_pci_remove(struct genwqe_dev *cd) /** * genwqe_probe() - Device initialization - * @pdev: PCI device information struct + * @pci_dev: PCI device information struct + * @id: PCI device ID * * Callable for multiple cards. This function is called on bind. * @@ -1214,6 +1227,7 @@ static int genwqe_probe(struct pci_dev *pci_dev, /** * genwqe_remove() - Called when device is removed (hot-plugable) + * @pci_dev: PCI device information struct * * Or when driver is unloaded respecitively when unbind is done. */ @@ -1233,8 +1247,10 @@ static void genwqe_remove(struct pci_dev *pci_dev) genwqe_dev_free(cd); } -/* +/** * genwqe_err_error_detected() - Error detection callback + * @pci_dev: PCI device information struct + * @state: PCI channel state * * This callback is called by the PCI subsystem whenever a PCI bus * error is detected. @@ -1342,6 +1358,8 @@ static struct pci_driver genwqe_driver = { /** * genwqe_devnode() - Set default access mode for genwqe devices. + * @dev: Pointer to device (unused) + * @mode: Carrier to pass-back given mode (permissions) * * Default mode should be rw for everybody. Do not change default * device name. -- cgit v1.2.3-59-g8ed1b From 807062fc8b2bb113d0ebc9262d2e1a39121e060d Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 1 Jul 2020 09:58:52 +0100 Subject: misc: genwqe: card_dev: Whole host of kerneldoc fixes Including; add missing documentation for function arguments, re-ordering of #defines i.e. not placed between kerneldoc headers and the functions they are documenting, demotion of file header/comment from kerneldoc format and removal of documentation for non-existent args. Fixes the following W=1 kernel build warnings: drivers/misc/genwqe/card_dev.c:33: warning: Function parameter or member 'cd' not described in 'genwqe_open_files' drivers/misc/genwqe/card_dev.c:98: warning: Function parameter or member 'virt_addr' not described in 'genwqe_search_pin' drivers/misc/genwqe/card_dev.c:98: warning: Excess function parameter 'dma_addr' description in 'genwqe_search_pin' drivers/misc/genwqe/card_dev.c:154: warning: Function parameter or member 'virt_addr' not described in '__genwqe_search_mapping' drivers/misc/genwqe/card_dev.c:256: warning: Function parameter or member 'cd' not described in 'genwqe_kill_fasync' drivers/misc/genwqe/card_dev.c:256: warning: Function parameter or member 'sig' not described in 'genwqe_kill_fasync' drivers/misc/genwqe/card_dev.c:387: warning: Function parameter or member 'vma' not described in 'genwqe_vma_close' drivers/misc/genwqe/card_dev.c:430: warning: Function parameter or member 'filp' not described in 'genwqe_mmap' drivers/misc/genwqe/card_dev.c:430: warning: Function parameter or member 'vma' not described in 'genwqe_mmap' drivers/misc/genwqe/card_dev.c:495: warning: Excess function parameter 'cd' description in 'FLASH_BLOCK' drivers/misc/genwqe/card_dev.c:495: warning: Excess function parameter 'load' description in 'FLASH_BLOCK' drivers/misc/genwqe/card_dev.c:827: warning: Function parameter or member 'cfile' not described in 'ddcb_cmd_cleanup' drivers/misc/genwqe/card_dev.c:827: warning: Function parameter or member 'req' not described in 'ddcb_cmd_cleanup' drivers/misc/genwqe/card_dev.c:854: warning: Function parameter or member 'cfile' not described in 'ddcb_cmd_fixups' drivers/misc/genwqe/card_dev.c:854: warning: Function parameter or member 'req' not described in 'ddcb_cmd_fixups' drivers/misc/genwqe/card_dev.c:984: warning: Function parameter or member 'cfile' not described in 'genwqe_execute_ddcb' drivers/misc/genwqe/card_dev.c:984: warning: Function parameter or member 'cmd' not described in 'genwqe_execute_ddcb' drivers/misc/genwqe/card_dev.c:1350: warning: Function parameter or member 'cd' not described in 'genwqe_device_remove' Cc: Michael Jung Cc: Michael Ruettger Cc: Frank Haverkamp Cc: Joerg-Stephan Vogt Signed-off-by: Lee Jones Reviewed-by: Frank Haverkamp Link: https://lore.kernel.org/r/20200701085853.164358-20-lee.jones@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/genwqe/card_dev.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/drivers/misc/genwqe/card_dev.c b/drivers/misc/genwqe/card_dev.c index 040a0bda3125..55fc5b80e649 100644 --- a/drivers/misc/genwqe/card_dev.c +++ b/drivers/misc/genwqe/card_dev.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -/** +/* * IBM Accelerator Family 'GenWQE' * * (C) Copyright IBM Corp. 2013 @@ -87,7 +87,7 @@ static int genwqe_del_pin(struct genwqe_file *cfile, struct dma_mapping *m) * @cfile: Descriptor of opened file * @u_addr: User virtual address * @size: Size of buffer - * @dma_addr: DMA address to be updated + * @virt_addr: Virtual address to be updated * * Return: Pointer to the corresponding mapping NULL if not found */ @@ -144,6 +144,7 @@ static void __genwqe_del_mapping(struct genwqe_file *cfile, * @u_addr: user virtual address * @size: size of buffer * @dma_addr: DMA address to be updated + * @virt_addr: Virtual address to be updated * Return: Pointer to the corresponding mapping NULL if not found */ static struct dma_mapping *__genwqe_search_mapping(struct genwqe_file *cfile, @@ -249,6 +250,8 @@ static void genwqe_remove_pinnings(struct genwqe_file *cfile) /** * genwqe_kill_fasync() - Send signal to all processes with open GenWQE files + * @cd: GenWQE device information + * @sig: Signal to send out * * E.g. genwqe_send_signal(cd, SIGIO); */ @@ -380,6 +383,7 @@ static void genwqe_vma_open(struct vm_area_struct *vma) /** * genwqe_vma_close() - Called each time when vma is unmapped + * @vma: VMA area to close * * Free memory which got allocated by GenWQE mmap(). */ @@ -416,6 +420,8 @@ static const struct vm_operations_struct genwqe_vma_ops = { /** * genwqe_mmap() - Provide contignous buffers to userspace + * @filp: File pointer (unused) + * @vma: VMA area to map * * We use mmap() to allocate contignous buffers used for DMA * transfers. After the buffer is allocated we remap it to user-space @@ -484,16 +490,15 @@ static int genwqe_mmap(struct file *filp, struct vm_area_struct *vma) return rc; } +#define FLASH_BLOCK 0x40000 /* we use 256k blocks */ + /** * do_flash_update() - Excute flash update (write image or CVPD) - * @cd: genwqe device + * @cfile: Descriptor of opened file * @load: details about image load * * Return: 0 if successful */ - -#define FLASH_BLOCK 0x40000 /* we use 256k blocks */ - static int do_flash_update(struct genwqe_file *cfile, struct genwqe_bitstream *load) { @@ -820,6 +825,8 @@ static int genwqe_unpin_mem(struct genwqe_file *cfile, struct genwqe_mem *m) /** * ddcb_cmd_cleanup() - Remove dynamically created fixup entries + * @cfile: Descriptor of opened file + * @req: DDCB work request * * Only if there are any. Pinnings are not removed. */ @@ -844,6 +851,8 @@ static int ddcb_cmd_cleanup(struct genwqe_file *cfile, struct ddcb_requ *req) /** * ddcb_cmd_fixups() - Establish DMA fixups/sglists for user memory references + * @cfile: Descriptor of opened file + * @req: DDCB work request * * Before the DDCB gets executed we need to handle the fixups. We * replace the user-space addresses with DMA addresses or do @@ -974,6 +983,8 @@ static int ddcb_cmd_fixups(struct genwqe_file *cfile, struct ddcb_requ *req) /** * genwqe_execute_ddcb() - Execute DDCB using userspace address fixups + * @cfile: Descriptor of opened file + * @cmd: Command identifier (passed from user) * * The code will build up the translation tables or lookup the * contignous memory allocation table to find the right translations @@ -1339,6 +1350,7 @@ static int genwqe_inform_and_stop_processes(struct genwqe_dev *cd) /** * genwqe_device_remove() - Remove genwqe's char device + * @cd: GenWQE device information * * This function must be called after the client devices are removed * because it will free the major/minor number range for the genwqe -- cgit v1.2.3-59-g8ed1b From 364b40c05d9a1f78bcdc08b5ba91afa71ed05185 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 1 Jul 2020 09:58:53 +0100 Subject: misc: genwqe: card_utils: Whole a plethora of documentation issues Including; demoting file header from kerneldoc to standard comment/ header, adding a variety of missing function argument documentation, repairing formatting (kerneldoc does not like blank lines) and the demotion of a kerneldoc header which shows no interest in providing documentation for any of its arguments. Fixes this the following W=1 issues: drivers/misc/genwqe/card_utils.c:37: warning: Incorrect use of kernel-doc format: * __genwqe_writeq() - Write 64-bit register drivers/misc/genwqe/card_utils.c:45: warning: Function parameter or member 'cd' not described in '__genwqe_writeq' drivers/misc/genwqe/card_utils.c:45: warning: Function parameter or member 'byte_offs' not described in '__genwqe_writeq' drivers/misc/genwqe/card_utils.c:45: warning: Function parameter or member 'val' not described in '__genwqe_writeq' drivers/misc/genwqe/card_utils.c:136: warning: Function parameter or member 'cd' not described in 'genwqe_read_app_id' drivers/misc/genwqe/card_utils.c:136: warning: Function parameter or member 'app_name' not described in 'genwqe_read_app_id' drivers/misc/genwqe/card_utils.c:136: warning: Function parameter or member 'len' not described in 'genwqe_read_app_id' drivers/misc/genwqe/card_utils.c:186: warning: bad line: drivers/misc/genwqe/card_utils.c:290: warning: Function parameter or member 'cd' not described in 'genwqe_alloc_sync_sgl' drivers/misc/genwqe/card_utils.c:290: warning: Function parameter or member 'sgl' not described in 'genwqe_alloc_sync_sgl' drivers/misc/genwqe/card_utils.c:290: warning: Function parameter or member 'user_addr' not described in 'genwqe_alloc_sync_sgl' drivers/misc/genwqe/card_utils.c:290: warning: Function parameter or member 'user_size' not described in 'genwqe_alloc_sync_sgl' drivers/misc/genwqe/card_utils.c:290: warning: Function parameter or member 'write' not described in 'genwqe_alloc_sync_sgl' drivers/misc/genwqe/card_utils.c:469: warning: Function parameter or member 'cd' not described in 'genwqe_free_sync_sgl' drivers/misc/genwqe/card_utils.c:469: warning: Function parameter or member 'sgl' not described in 'genwqe_free_sync_sgl' drivers/misc/genwqe/card_utils.c:716: warning: Function parameter or member 'count' not described in 'genwqe_set_interrupt_capability' drivers/misc/genwqe/card_utils.c:747: warning: Function parameter or member 'idx' not described in 'set_reg_idx' drivers/misc/genwqe/card_utils.c:747: warning: Excess function parameter 'index' description in 'set_reg_idx' drivers/misc/genwqe/card_utils.c:823: warning: Function parameter or member 'cd' not described in 'genwqe_ffdc_buff_size' drivers/misc/genwqe/card_utils.c:823: warning: Function parameter or member 'uid' not described in 'genwqe_ffdc_buff_size' drivers/misc/genwqe/card_utils.c:877: warning: Function parameter or member 'cd' not described in 'genwqe_ffdc_buff_read' drivers/misc/genwqe/card_utils.c:877: warning: Function parameter or member 'uid' not described in 'genwqe_ffdc_buff_read' drivers/misc/genwqe/card_utils.c:877: warning: Function parameter or member 'regs' not described in 'genwqe_ffdc_buff_read' drivers/misc/genwqe/card_utils.c:877: warning: Function parameter or member 'max_regs' not described in 'genwqe_ffdc_buff_read' drivers/misc/genwqe/card_utils.c:964: warning: Function parameter or member 'cd' not described in 'genwqe_write_vreg' drivers/misc/genwqe/card_utils.c:964: warning: Function parameter or member 'reg' not described in 'genwqe_write_vreg' drivers/misc/genwqe/card_utils.c:964: warning: Function parameter or member 'val' not described in 'genwqe_write_vreg' drivers/misc/genwqe/card_utils.c:964: warning: Function parameter or member 'func' not described in 'genwqe_write_vreg' drivers/misc/genwqe/card_utils.c:977: warning: Function parameter or member 'cd' not described in 'genwqe_read_vreg' drivers/misc/genwqe/card_utils.c:977: warning: Function parameter or member 'reg' not described in 'genwqe_read_vreg' drivers/misc/genwqe/card_utils.c:977: warning: Function parameter or member 'func' not described in 'genwqe_read_vreg' drivers/misc/genwqe/card_utils.c:995: warning: Function parameter or member 'cd' not described in 'genwqe_base_clock_frequency' drivers/misc/genwqe/card_utils.c:1012: warning: Function parameter or member 'cd' not described in 'genwqe_stop_traps' drivers/misc/genwqe/card_utils.c:1022: warning: Function parameter or member 'cd' not described in 'genwqe_start_traps' Cc: Michael Jung Cc: Michael Ruettger Cc: Frank Haverkamp Cc: Joerg-Stephan Vogt Signed-off-by: Lee Jones Reviewed-by: Frank Haverkamp # Please enter the commit message for your changes. Lines starting # with '#' will be ignored, and an empty message aborts the commit. # # Date: Mon Jun 29 10:30:56 2020 +0100 # # On branch tb-mfd-fix-warnings # Changes to be committed: # modified: drivers/misc/genwqe/card_utils.c # # Untracked files: # qemu-i2c-devs.txt # Link: https://lore.kernel.org/r/20200701085853.164358-21-lee.jones@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/genwqe/card_utils.c | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/drivers/misc/genwqe/card_utils.c b/drivers/misc/genwqe/card_utils.c index 77c21caf2acd..039b923d1d60 100644 --- a/drivers/misc/genwqe/card_utils.c +++ b/drivers/misc/genwqe/card_utils.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -/** +/* * IBM Accelerator Family 'GenWQE' * * (C) Copyright IBM Corp. 2013 @@ -129,6 +129,9 @@ u32 __genwqe_readl(struct genwqe_dev *cd, u64 byte_offs) /** * genwqe_read_app_id() - Extract app_id + * @cd: genwqe device descriptor + * @app_name: carrier used to pass-back name + * @len: length of data for name * * app_unitcfg need to be filled with valid data first */ @@ -183,7 +186,7 @@ void genwqe_init_crc32(void) * @init: initial crc (0xffffffff at start) * * polynomial = x^32 * + x^29 + x^18 + x^14 + x^3 + 1 (0x20044009) - + * * Example: 4 bytes 0x01 0x02 0x03 0x04 with init=0xffffffff should * result in a crc32 of 0xf33cb7d3. * @@ -277,7 +280,7 @@ static int genwqe_sgl_size(int num_pages) return roundup(len, PAGE_SIZE); } -/** +/* * genwqe_alloc_sync_sgl() - Allocate memory for sgl and overlapping pages * * Allocates memory for sgl and overlapping pages. Pages which might @@ -460,6 +463,8 @@ int genwqe_setup_sgl(struct genwqe_dev *cd, struct genwqe_sgl *sgl, /** * genwqe_free_sync_sgl() - Free memory for sgl and overlapping pages + * @cd: genwqe device descriptor + * @sgl: scatter gather list describing user-space memory * * After the DMA transfer has been completed we free the memory for * the sgl and the cached pages. Data is being transferred from cached @@ -710,6 +715,7 @@ int genwqe_read_softreset(struct genwqe_dev *cd) /** * genwqe_set_interrupt_capability() - Configure MSI capability structure * @cd: pointer to the device + * @count: number of vectors to allocate * Return: 0 if no error */ int genwqe_set_interrupt_capability(struct genwqe_dev *cd, int count) @@ -738,7 +744,7 @@ void genwqe_reset_interrupt_capability(struct genwqe_dev *cd) * @i: index to desired entry * @m: maximum possible entries * @addr: addr which is read - * @index: index in debug array + * @idx: index in debug array * @val: read value */ static int set_reg_idx(struct genwqe_dev *cd, struct genwqe_reg *r, @@ -818,6 +824,8 @@ int genwqe_read_ffdc_regs(struct genwqe_dev *cd, struct genwqe_reg *regs, /** * genwqe_ffdc_buff_size() - Calculates the number of dump registers + * @cd: genwqe device descriptor + * @uid: unit ID */ int genwqe_ffdc_buff_size(struct genwqe_dev *cd, int uid) { @@ -871,6 +879,10 @@ int genwqe_ffdc_buff_size(struct genwqe_dev *cd, int uid) /** * genwqe_ffdc_buff_read() - Implements LogoutExtendedErrorRegisters procedure + * @cd: genwqe device descriptor + * @uid: unit ID + * @regs: register information + * @max_regs: number of register entries */ int genwqe_ffdc_buff_read(struct genwqe_dev *cd, int uid, struct genwqe_reg *regs, unsigned int max_regs) @@ -956,6 +968,10 @@ int genwqe_ffdc_buff_read(struct genwqe_dev *cd, int uid, /** * genwqe_write_vreg() - Write register in virtual window + * @cd: genwqe device descriptor + * @reg: register (byte) offset within BAR + * @val: value to write + * @func: PCI virtual function * * Note, these registers are only accessible to the PF through the * VF-window. It is not intended for the VF to access. @@ -969,6 +985,9 @@ int genwqe_write_vreg(struct genwqe_dev *cd, u32 reg, u64 val, int func) /** * genwqe_read_vreg() - Read register in virtual window + * @cd: genwqe device descriptor + * @reg: register (byte) offset within BAR + * @func: PCI virtual function * * Note, these registers are only accessible to the PF through the * VF-window. It is not intended for the VF to access. @@ -981,6 +1000,7 @@ u64 genwqe_read_vreg(struct genwqe_dev *cd, u32 reg, int func) /** * genwqe_base_clock_frequency() - Deteremine base clock frequency of the card + * @cd: genwqe device descriptor * * Note: From a design perspective it turned out to be a bad idea to * use codes here to specifiy the frequency/speed values. An old @@ -1005,6 +1025,7 @@ int genwqe_base_clock_frequency(struct genwqe_dev *cd) /** * genwqe_stop_traps() - Stop traps + * @cd: genwqe device descriptor * * Before reading out the analysis data, we need to stop the traps. */ @@ -1015,6 +1036,7 @@ void genwqe_stop_traps(struct genwqe_dev *cd) /** * genwqe_start_traps() - Start traps + * @cd: genwqe device descriptor * * After having read the data, we can/must enable the traps again. */ -- cgit v1.2.3-59-g8ed1b From 7adb55bd4ee53f60e026575f80a12749750e4e5c Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 1 Jul 2020 09:30:49 +0100 Subject: misc: genwqe: card_ddcb: Fix a variety of kerneldoc issues Including; demoting file header from kerneldoc to standard comment/ header, adding a variety of missing function argument documentation, re-ordering of #defines i.e. not placed between kerneldoc headers and the functions they are documenting and re-documenting of changed/ renamed arguments. Fixes the following W=1 kernel build warnings: drivers/misc/genwqe/card_ddcb.c:83: warning: Function parameter or member 'queue' not described in 'queue_empty' drivers/misc/genwqe/card_ddcb.c:262: warning: Excess function parameter 'cd' description in 'RET_DDCB_APPENDED' drivers/misc/genwqe/card_ddcb.c:262: warning: Excess function parameter 'queue' description in 'RET_DDCB_APPENDED' drivers/misc/genwqe/card_ddcb.c:262: warning: Excess function parameter 'ddcb_no' description in 'RET_DDCB_APPENDED' drivers/misc/genwqe/card_ddcb.c:329: warning: Function parameter or member 'req' not described in 'copy_ddcb_results' drivers/misc/genwqe/card_ddcb.c:329: warning: Function parameter or member 'ddcb_no' not described in 'copy_ddcb_results' drivers/misc/genwqe/card_ddcb.c:364: warning: Function parameter or member 'queue' not described in 'genwqe_check_ddcb_queue' drivers/misc/genwqe/card_ddcb.c:565: warning: Function parameter or member 'queue' not described in 'get_next_ddcb' drivers/misc/genwqe/card_ddcb.c:565: warning: Function parameter or member 'num' not described in 'get_next_ddcb' drivers/misc/genwqe/card_ddcb.c:909: warning: Function parameter or member 'cmd' not described in '__genwqe_execute_raw_ddcb' drivers/misc/genwqe/card_ddcb.c:909: warning: Excess function parameter 'req' description in '__genwqe_execute_raw_ddcb' drivers/misc/genwqe/card_ddcb.c:972: warning: Function parameter or member 'cd' not described in 'genwqe_next_ddcb_ready' drivers/misc/genwqe/card_ddcb.c:1002: warning: Function parameter or member 'cd' not described in 'genwqe_ddcbs_in_flight' drivers/misc/genwqe/card_ddcb.c:1181: warning: Function parameter or member 'data' not described in 'genwqe_card_thread' drivers/misc/genwqe/card_ddcb.c:1308: warning: Function parameter or member 'cd' not described in 'queue_wake_up_all' drivers/misc/genwqe/card_ddcb.c:1333: warning: Function parameter or member 'cd' not described in 'genwqe_finish_queue' Cc: Michael Jung Cc: Michael Ruettger Cc: Frank Haverkamp Cc: Joerg-Stephan Vogt Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20200701083118.45744-2-lee.jones@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/genwqe/card_ddcb.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/drivers/misc/genwqe/card_ddcb.c b/drivers/misc/genwqe/card_ddcb.c index 905106579935..0db4000dedf2 100644 --- a/drivers/misc/genwqe/card_ddcb.c +++ b/drivers/misc/genwqe/card_ddcb.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -/** +/* * IBM Accelerator Family 'GenWQE' * * (C) Copyright IBM Corp. 2013 @@ -244,10 +244,13 @@ static int ddcb_requ_finished(struct genwqe_dev *cd, struct ddcb_requ *req) (cd->card_state != GENWQE_CARD_USED); } +#define RET_DDCB_APPENDED 1 +#define RET_DDCB_TAPPED 2 /** * enqueue_ddcb() - Enqueue a DDCB * @cd: pointer to genwqe device descriptor * @queue: queue this operation should be done on + * @pddcb: pointer to ddcb structure * @ddcb_no: pointer to ddcb number being tapped * * Start execution of DDCB by tapping or append to queue via NEXT @@ -259,9 +262,6 @@ static int ddcb_requ_finished(struct genwqe_dev *cd, struct ddcb_requ *req) * Return: 1 if new DDCB is appended to previous * 2 if DDCB queue is tapped via register/simulation */ -#define RET_DDCB_APPENDED 1 -#define RET_DDCB_TAPPED 2 - static int enqueue_ddcb(struct genwqe_dev *cd, struct ddcb_queue *queue, struct ddcb *pddcb, int ddcb_no) { @@ -316,6 +316,8 @@ static int enqueue_ddcb(struct genwqe_dev *cd, struct ddcb_queue *queue, /** * copy_ddcb_results() - Copy output state from real DDCB to request + * @req: pointer to requsted DDCB parameters + * @ddcb_no: pointer to ddcb number being tapped * * Copy DDCB ASV to request struct. There is no endian * conversion made, since data structure in ASV is still @@ -356,6 +358,7 @@ static void copy_ddcb_results(struct ddcb_requ *req, int ddcb_no) /** * genwqe_check_ddcb_queue() - Checks DDCB queue for completed work equests. * @cd: pointer to genwqe device descriptor + * @queue: queue to be checked * * Return: Number of DDCBs which were finished */ @@ -553,6 +556,8 @@ int __genwqe_wait_ddcb(struct genwqe_dev *cd, struct ddcb_requ *req) /** * get_next_ddcb() - Get next available DDCB * @cd: pointer to genwqe device descriptor + * @queue: DDCB queue + * @num: internal DDCB number * * DDCB's content is completely cleared but presets for PRE and * SEQNUM. This function must only be called when ddcb_lock is held. @@ -900,7 +905,7 @@ int __genwqe_enqueue_ddcb(struct genwqe_dev *cd, struct ddcb_requ *req, /** * __genwqe_execute_raw_ddcb() - Setup and execute DDCB * @cd: pointer to genwqe device descriptor - * @req: user provided DDCB request + * @cmd: user provided DDCB command * @f_flags: file mode: blocking, non-blocking */ int __genwqe_execute_raw_ddcb(struct genwqe_dev *cd, @@ -965,6 +970,7 @@ int __genwqe_execute_raw_ddcb(struct genwqe_dev *cd, /** * genwqe_next_ddcb_ready() - Figure out if the next DDCB is already finished + * @cd: pointer to genwqe device descriptor * * We use this as condition for our wait-queue code. */ @@ -993,6 +999,7 @@ static int genwqe_next_ddcb_ready(struct genwqe_dev *cd) /** * genwqe_ddcbs_in_flight() - Check how many DDCBs are in flight + * @cd: pointer to genwqe device descriptor * * Keep track on the number of DDCBs which ware currently in the * queue. This is needed for statistics as well as conditon if we want @@ -1171,6 +1178,7 @@ static irqreturn_t genwqe_vf_isr(int irq, void *dev_id) /** * genwqe_card_thread() - Work thread for the DDCB queue + * @data: pointer to genwqe device descriptor * * The idea is to check if there are DDCBs in processing. If there are * some finished DDCBs, we process them and wakeup the @@ -1299,6 +1307,7 @@ int genwqe_setup_service_layer(struct genwqe_dev *cd) /** * queue_wake_up_all() - Handles fatal error case + * @cd: pointer to genwqe device descriptor * * The PCI device got unusable and we have to stop all pending * requests as fast as we can. The code after this must purge the @@ -1323,6 +1332,7 @@ static int queue_wake_up_all(struct genwqe_dev *cd) /** * genwqe_finish_queue() - Remove any genwqe devices and user-interfaces + * @cd: pointer to genwqe device descriptor * * Relies on the pre-condition that there are no users of the card * device anymore e.g. with open file-descriptors. -- cgit v1.2.3-59-g8ed1b From ac36fdb1b92e885a3b523265692c5b1954f8f61f Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 1 Jul 2020 09:30:50 +0100 Subject: misc: genwqe: card_sysfs: Demote function/file headers from kerneldoc There has been no attempt to provide documentation for these function's arguments, so align with the remainder of the file and just treat them as standards function headers. Fixes the following W=1 kernel build warnings: drivers/misc/genwqe/card_sysfs.c:32: warning: cannot understand function prototype: 'const char * const genwqe_types[] = ' drivers/misc/genwqe/card_sysfs.c:150: warning: Function parameter or member 'dev' not described in 'curr_bitstream_show' drivers/misc/genwqe/card_sysfs.c:150: warning: Function parameter or member 'attr' not described in 'curr_bitstream_show' drivers/misc/genwqe/card_sysfs.c:150: warning: Function parameter or member 'buf' not described in 'curr_bitstream_show' drivers/misc/genwqe/card_sysfs.c:166: warning: Function parameter or member 'dev' not described in 'next_bitstream_show' drivers/misc/genwqe/card_sysfs.c:166: warning: Function parameter or member 'attr' not described in 'next_bitstream_show' drivers/misc/genwqe/card_sysfs.c:166: warning: Function parameter or member 'buf' not described in 'next_bitstream_show' drivers/misc/genwqe/card_sysfs.c:271: warning: Function parameter or member 'kobj' not described in 'genwqe_is_visible' drivers/misc/genwqe/card_sysfs.c:271: warning: Function parameter or member 'attr' not described in 'genwqe_is_visible' drivers/misc/genwqe/card_sysfs.c:271: warning: Function parameter or member 'n' not described in 'genwqe_is_visible' Cc: Michael Jung Cc: Michael Ruettger Cc: Frank Haverkamp Cc: Joerg-Stephan Vogt Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20200701083118.45744-3-lee.jones@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/genwqe/card_sysfs.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/misc/genwqe/card_sysfs.c b/drivers/misc/genwqe/card_sysfs.c index 28a3fb1533f7..b2f115602523 100644 --- a/drivers/misc/genwqe/card_sysfs.c +++ b/drivers/misc/genwqe/card_sysfs.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -/** +/* * IBM Accelerator Family 'GenWQE' * * (C) Copyright IBM Corp. 2013 @@ -129,7 +129,7 @@ static ssize_t base_clock_show(struct device *dev, } static DEVICE_ATTR_RO(base_clock); -/** +/* * curr_bitstream_show() - Show the current bitstream id * * There is a bug in some old versions of the CPLD which selects the @@ -156,7 +156,7 @@ static ssize_t curr_bitstream_show(struct device *dev, } static DEVICE_ATTR_RO(curr_bitstream); -/** +/* * next_bitstream_show() - Show the next activated bitstream * * IO_SLC_CFGREG_SOFTRESET: This register can only be accessed by the PF. @@ -260,7 +260,7 @@ static struct attribute *genwqe_normal_attributes[] = { NULL, }; -/** +/* * genwqe_is_visible() - Determine if sysfs attribute should be visible or not * * VFs have restricted mmio capabilities, so not all sysfs entries -- cgit v1.2.3-59-g8ed1b From f5429c2cecfebae0b8ee6ba8e591a9d5b34f612c Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 1 Jul 2020 09:30:51 +0100 Subject: misc: genwqe: card_debugfs: Demote file header from kerneldoc Kerneldoc does not understand file headers. There is therefor no reason for them to use the syntax. Fixes the following W=1 kernel build warnings: drivers/misc/genwqe/card_debugfs.c:30: warning: Function parameter or member 's' not described in 'dbg_uidn_show' drivers/misc/genwqe/card_debugfs.c:30: warning: Function parameter or member 'regs' not described in 'dbg_uidn_show' drivers/misc/genwqe/card_debugfs.c:30: warning: Function parameter or member 'entries' not described in 'dbg_uidn_show' Cc: Michael Jung Cc: Michael Ruettger Cc: Frank Haverkamp Cc: Joerg-Stephan Vogt Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20200701083118.45744-4-lee.jones@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/genwqe/card_debugfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/genwqe/card_debugfs.c b/drivers/misc/genwqe/card_debugfs.c index 1b5b82e65268..491fb4482da2 100644 --- a/drivers/misc/genwqe/card_debugfs.c +++ b/drivers/misc/genwqe/card_debugfs.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -/** +/* * IBM Accelerator Family 'GenWQE' * * (C) Copyright IBM Corp. 2013 -- cgit v1.2.3-59-g8ed1b From 95ef32cdef161d8f4085967049a908558aa8797c Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 1 Jul 2020 09:30:52 +0100 Subject: misc: sgi-xp: xp_main: Staticify local functions xp_init() and xp_exit() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These functions are not exported and no external prototypes exist Fixes W=1 kernel build warning(s): drivers/misc/sgi-xp/xp_main.c:227:1: warning: no previous prototype for ‘xp_init’ [-Wmissing-prototypes] 227 | xp_init(void) | ^~~~~~~ drivers/misc/sgi-xp/xp_main.c:250:1: warning: no previous prototype for ‘xp_exit’ [-Wmissing-prototypes] 250 | xp_exit(void) | ^~~~~~~ Cc: Cliff Whickman Cc: Robin Holt Cc: Dean Nelson Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20200701083118.45744-5-lee.jones@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/sgi-xp/xp_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/misc/sgi-xp/xp_main.c b/drivers/misc/sgi-xp/xp_main.c index 5fd94d836070..61b03fcefb13 100644 --- a/drivers/misc/sgi-xp/xp_main.c +++ b/drivers/misc/sgi-xp/xp_main.c @@ -223,7 +223,7 @@ xpc_disconnect(int ch_number) } EXPORT_SYMBOL_GPL(xpc_disconnect); -int __init +static int __init xp_init(void) { enum xp_retval ret; @@ -246,7 +246,7 @@ xp_init(void) module_init(xp_init); -void __exit +static void __exit xp_exit(void) { if (is_uv()) -- cgit v1.2.3-59-g8ed1b From 63969cf28873d6c206fb34f2e4120fe7d7679e1e Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 1 Jul 2020 09:30:53 +0100 Subject: misc: ibmasm: event: Demote function headers from kerneldoc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The correct format is not used and no attempt has been made to document the function arguments. Makes sense to just demote the header back down to a simple comment. Fixes the following W=1 warnings: drivers/misc/ibmasm/event.c:44: warning: Function parameter or member 'sp' not described in 'ibmasm_receive_event' drivers/misc/ibmasm/event.c:44: warning: Function parameter or member 'data' not described in 'ibmasm_receive_event' drivers/misc/ibmasm/event.c:44: warning: Function parameter or member 'data_size' not described in 'ibmasm_receive_event' drivers/misc/ibmasm/event.c:78: warning: Function parameter or member 'sp' not described in 'ibmasm_get_next_event' drivers/misc/ibmasm/event.c:78: warning: Function parameter or member 'reader' not described in 'ibmasm_get_next_event' Cc: "Max Asböck" Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20200701083118.45744-6-lee.jones@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/ibmasm/event.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/misc/ibmasm/event.c b/drivers/misc/ibmasm/event.c index 974d63f5a4dd..40ce75f8970c 100644 --- a/drivers/misc/ibmasm/event.c +++ b/drivers/misc/ibmasm/event.c @@ -31,7 +31,7 @@ static void wake_up_event_readers(struct service_processor *sp) wake_up_interruptible(&reader->wait); } -/** +/* * receive_event * Called by the interrupt handler when a dot command of type sp_event is * received. @@ -68,7 +68,7 @@ static inline int event_available(struct event_buffer *b, struct event_reader *r return (r->next_serial_number < b->next_serial_number); } -/** +/* * get_next_event * Called by event readers (initiated from user space through the file * system). -- cgit v1.2.3-59-g8ed1b From b7df87cfe3d13596820afdda73338d44172a643e Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 1 Jul 2020 09:30:54 +0100 Subject: misc: ibmasm: command: Demote function headers from kerneldoc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The correct format is not used and no attempt has been made to document the function arguments. Makes sense to just demote the header back down to a simple comment. Fixes the following W=1 warnings: drivers/misc/ibmasm/command.c:106: warning: Function parameter or member 'sp' not described in 'ibmasm_exec_command' drivers/misc/ibmasm/command.c:106: warning: Function parameter or member 'cmd' not described in 'ibmasm_exec_command' drivers/misc/ibmasm/command.c:149: warning: Function parameter or member 'cmd' not described in 'ibmasm_wait_for_response' drivers/misc/ibmasm/command.c:149: warning: Function parameter or member 'timeout' not described in 'ibmasm_wait_for_response' drivers/misc/ibmasm/command.c:162: warning: Function parameter or member 'sp' not described in 'ibmasm_receive_command_response' drivers/misc/ibmasm/command.c:162: warning: Function parameter or member 'response' not described in 'ibmasm_receive_command_response' drivers/misc/ibmasm/command.c:162: warning: Function parameter or member 'size' not described in 'ibmasm_receive_command_response' Cc: "Max Asböck" Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20200701083118.45744-7-lee.jones@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/ibmasm/command.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/misc/ibmasm/command.c b/drivers/misc/ibmasm/command.c index 2863657fa268..733dd30fbacc 100644 --- a/drivers/misc/ibmasm/command.c +++ b/drivers/misc/ibmasm/command.c @@ -94,7 +94,7 @@ static inline void do_exec_command(struct service_processor *sp) } } -/** +/* * exec_command * send a command to a service processor * Commands are executed sequentially. One command (sp->current_command) @@ -140,7 +140,7 @@ static void exec_next_command(struct service_processor *sp) } } -/** +/* * Sleep until a command has failed or a response has been received * and the command status been updated by the interrupt handler. * (see receive_response). @@ -153,7 +153,7 @@ void ibmasm_wait_for_response(struct command *cmd, int timeout) timeout * HZ); } -/** +/* * receive_command_response * called by the interrupt handler when a dot command of type command_response * was received. -- cgit v1.2.3-59-g8ed1b From e0a6ad87d088869181a27c08a131cb891ea3dd68 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 1 Jul 2020 09:30:56 +0100 Subject: misc: ibmasm: r_heartbeat: Demote function headers from kerneldoc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The correct format is not used and no attempt has been made to document the function arguments. Makes sense to just demote the header back down to a simple comment. Fixes the following W=1 warnings: drivers/misc/ibmasm/r_heartbeat.c:49: warning: Function parameter or member 'sp' not described in 'ibmasm_start_reverse_heartbeat' drivers/misc/ibmasm/r_heartbeat.c:49: warning: Function parameter or member 'rhb' not described in 'ibmasm_start_reverse_heartbeat' Cc: "Max Asböck" Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20200701083118.45744-9-lee.jones@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/ibmasm/r_heartbeat.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/ibmasm/r_heartbeat.c b/drivers/misc/ibmasm/r_heartbeat.c index 6567df638ea9..21c9b6a6f2c3 100644 --- a/drivers/misc/ibmasm/r_heartbeat.c +++ b/drivers/misc/ibmasm/r_heartbeat.c @@ -39,7 +39,7 @@ void ibmasm_init_reverse_heartbeat(struct service_processor *sp, struct reverse_ rhb->stopped = 0; } -/** +/* * start_reverse_heartbeat * Loop forever, sending a reverse heartbeat dot command to the service * processor, then sleeping. The loop comes to an end if the service -- cgit v1.2.3-59-g8ed1b From 9c03520ea0bb92170aca87fb0a8449657a753796 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 1 Jul 2020 09:30:57 +0100 Subject: misc: mic: host: mic_x100: Move declaration of mic_x100_intr_init[] into c-file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mic_x100_intr_init[] is only ever used in mic_x100.c, so instead of listing to the compiler complain about unused static arrays simply move it into the c-file for direct consumption. This way it can stay 'static'. Fixes the following W=1 kernel build warning: In file included from drivers/misc/mic/host/mic_main.c:17: drivers/misc/mic/host/mic_x100.h:70:18: warning: ‘mic_x100_intr_init’ defined but not used [-Wunused-const-variable=] 70 | static const u16 mic_x100_intr_init[] = { | ^~~~~~~~~~~~~~~~~~ Cc: Sudeep Dutt Cc: Ashutosh Dixit Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20200701083118.45744-10-lee.jones@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/host/mic_x100.c | 9 +++++++++ drivers/misc/mic/host/mic_x100.h | 9 --------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/misc/mic/host/mic_x100.c b/drivers/misc/mic/host/mic_x100.c index d18cda966912..843ce1375015 100644 --- a/drivers/misc/mic/host/mic_x100.c +++ b/drivers/misc/mic/host/mic_x100.c @@ -17,6 +17,15 @@ #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 diff --git a/drivers/misc/mic/host/mic_x100.h b/drivers/misc/mic/host/mic_x100.h index 1f727a6f609c..aebcaed6fa72 100644 --- a/drivers/misc/mic/host/mic_x100.h +++ b/drivers/misc/mic/host/mic_x100.h @@ -67,15 +67,6 @@ #define MIC_X100_FW_SIZE 5 #define MIC_X100_POSTCODE 0x242c -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, -}; - /* Host->Card(bootstrap) Interrupt Vector */ #define MIC_X100_BSP_INTERRUPT_VECTOR 229 -- cgit v1.2.3-59-g8ed1b From 779b961c4d752efb9a1b78e5dedc7936eb426ae0 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 1 Jul 2020 09:30:58 +0100 Subject: misc: ibmasm: dot_command: Demote function headers from kerneldoc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The correct format is not used and no attempt has been made to document the function arguments. Makes sense to just demote the header back down to a simple comment. Fixes the following W=1 warnings: drivers/misc/ibmasm/dot_command.c:18: warning: Function parameter or member 'sp' not described in 'ibmasm_receive_message' drivers/misc/ibmasm/dot_command.c:18: warning: Function parameter or member 'message' not described in 'ibmasm_receive_message' drivers/misc/ibmasm/dot_command.c:18: warning: Function parameter or member 'message_size' not described in 'ibmasm_receive_message' drivers/misc/ibmasm/dot_command.c:55: warning: Function parameter or member 'sp' not described in 'ibmasm_send_driver_vpd' drivers/misc/ibmasm/dot_command.c:111: warning: Function parameter or member 'sp' not described in 'ibmasm_send_os_state' drivers/misc/ibmasm/dot_command.c:111: warning: Function parameter or member 'os_state' not described in 'ibmasm_send_os_state' Cc: "Max Asböck" Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20200701083118.45744-11-lee.jones@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/ibmasm/dot_command.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/misc/ibmasm/dot_command.c b/drivers/misc/ibmasm/dot_command.c index 70273a4cb352..df389bd4c9df 100644 --- a/drivers/misc/ibmasm/dot_command.c +++ b/drivers/misc/ibmasm/dot_command.c @@ -10,7 +10,7 @@ #include "ibmasm.h" #include "dot_command.h" -/** +/* * Dispatch an incoming message to the specific handler for the message. * Called from interrupt context. */ @@ -48,7 +48,7 @@ void ibmasm_receive_message(struct service_processor *sp, void *message, int mes #define INIT_BUFFER_SIZE 32 -/** +/* * send the 4.3.5.10 dot command (driver VPD) to the service processor */ int ibmasm_send_driver_vpd(struct service_processor *sp) @@ -99,7 +99,7 @@ struct os_state_command { unsigned char data; }; -/** +/* * send the 4.3.6 dot command (os state) to the service processor * During driver init this function is called with os state "up". * This causes the service processor to start sending heartbeats the -- cgit v1.2.3-59-g8ed1b From f10957dba54a170f45614ea0f063a666d495b1b5 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 1 Jul 2020 09:30:59 +0100 Subject: misc: mic: host: mic_intr: Properly document function arguments mic_interrupt() and mic_setup_msix() have incomplete documentation. Fixes the following W=1 kernel build warnings: drivers/misc/mic/host/mic_intr.c:42: warning: Function parameter or member 'irq' not described in 'mic_interrupt' drivers/misc/mic/host/mic_intr.c:42: warning: Function parameter or member 'dev' not described in 'mic_interrupt' drivers/misc/mic/host/mic_intr.c:188: warning: Function parameter or member 'pdev' not described in 'mic_setup_msix' Cc: Sudeep Dutt Cc: Ashutosh Dixit Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20200701083118.45744-12-lee.jones@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/host/mic_intr.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/misc/mic/host/mic_intr.c b/drivers/misc/mic/host/mic_intr.c index 433d35dc1721..85b3221b5d40 100644 --- a/drivers/misc/mic/host/mic_intr.c +++ b/drivers/misc/mic/host/mic_intr.c @@ -37,6 +37,8 @@ static irqreturn_t mic_thread_fn(int irq, void *dev) /** * 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) { @@ -180,7 +182,7 @@ static u8 mic_unregister_intr_callback(struct mic_device *mdev, u32 idx) * 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. */ -- cgit v1.2.3-59-g8ed1b From 521482b53246139524d9c873b95dfd48f6b1add0 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 1 Jul 2020 09:31:00 +0100 Subject: misc: mic: host: mic_x100: Add missing descriptions to kerneldoc headers Fixes the following W=1 warnings: drivers/misc/mic/host/mic_x100.c:127: warning: Function parameter or member 'doorbell' not described in 'mic_x100_send_sbox_intr' drivers/misc/mic/host/mic_x100.c:148: warning: Function parameter or member 'doorbell' not described in 'mic_x100_send_rdmasr_intr' drivers/misc/mic/host/mic_x100.c:511: warning: Function parameter or member 'dma_addr' not described in 'mic_x100_smpt_set' drivers/misc/mic/host/mic_x100.c:511: warning: Function parameter or member 'index' not described in 'mic_x100_smpt_set' Cc: Sudeep Dutt Cc: Ashutosh Dixit Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20200701083118.45744-13-lee.jones@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/host/mic_x100.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/misc/mic/host/mic_x100.c b/drivers/misc/mic/host/mic_x100.c index 843ce1375015..f5536c1ad607 100644 --- a/drivers/misc/mic/host/mic_x100.c +++ b/drivers/misc/mic/host/mic_x100.c @@ -121,6 +121,7 @@ static void mic_x100_disable_interrupts(struct mic_device *mdev) /** * 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) @@ -142,6 +143,7 @@ static void mic_x100_send_sbox_intr(struct mic_device *mdev, /** * 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) @@ -503,6 +505,8 @@ static u32 mic_x100_get_postcode(struct mic_device *mdev) /** * 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. */ -- cgit v1.2.3-59-g8ed1b From 49f093f4b27ab73b0057e44c723665c0f58f16dd Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 1 Jul 2020 09:31:01 +0100 Subject: misc: mic: card: mic_debugfs: Demote function headers from kerneldoc The correct format is not used and no attempt has been made to document the function arguments. Makes sense to just demote the headers back down to simple comments. Fixes the following W=1 warnings: drivers/misc/mic/card/mic_debugfs.c:31: warning: Function parameter or member 's' not described in 'mic_intr_show' drivers/misc/mic/card/mic_debugfs.c:31: warning: Function parameter or member 'unused' not described in 'mic_intr_show' drivers/misc/mic/card/mic_debugfs.c:53: warning: Function parameter or member 'mdrv' not described in 'mic_create_card_debug_dir' drivers/misc/mic/card/mic_debugfs.c:67: warning: Function parameter or member 'mdrv' not described in 'mic_delete_card_debug_dir' Cc: Sudeep Dutt Cc: Ashutosh Dixit Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20200701083118.45744-14-lee.jones@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/card/mic_debugfs.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/misc/mic/card/mic_debugfs.c b/drivers/misc/mic/card/mic_debugfs.c index b58608829b18..4c326e8f4d99 100644 --- a/drivers/misc/mic/card/mic_debugfs.c +++ b/drivers/misc/mic/card/mic_debugfs.c @@ -24,7 +24,7 @@ /* 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) @@ -46,7 +46,7 @@ static int mic_intr_show(struct seq_file *s, void *unused) 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) @@ -60,7 +60,7 @@ void __init mic_create_card_debug_dir(struct mic_driver *mdrv) &mic_intr_fops); } -/** +/* * mic_delete_card_debug_dir - Uninitialize MIC debugfs entries. */ void mic_delete_card_debug_dir(struct mic_driver *mdrv) @@ -68,7 +68,7 @@ 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) @@ -76,7 +76,7 @@ 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) -- cgit v1.2.3-59-g8ed1b From 438ad36850d74ebb43500e26920b792b54f81a5f Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 1 Jul 2020 09:31:02 +0100 Subject: misc: mic: host: mic_debugfs: Demote function headers from kerneldoc The correct format is not used and no attempt has been made to document the function arguments. Makes sense to just demote the headers back down to simple comments. Fixes the following W=1 warnings: drivers/misc/mic/host/mic_debugfs.c:108: warning: Function parameter or member 'mdev' not described in 'mic_create_debug_dir' drivers/misc/mic/host/mic_debugfs.c:131: warning: Function parameter or member 'mdev' not described in 'mic_delete_debug_dir' Cc: Sudeep Dutt Cc: Ashutosh Dixit Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20200701083118.45744-15-lee.jones@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/host/mic_debugfs.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/misc/mic/host/mic_debugfs.c b/drivers/misc/mic/host/mic_debugfs.c index ab0db7a2ac8c..ffda740e20d5 100644 --- a/drivers/misc/mic/host/mic_debugfs.c +++ b/drivers/misc/mic/host/mic_debugfs.c @@ -101,7 +101,7 @@ static int mic_msi_irq_info_show(struct seq_file *s, void *pos) DEFINE_SHOW_ATTRIBUTE(mic_msi_irq_info); -/** +/* * mic_create_debug_dir - Initialize MIC debugfs entries. */ void mic_create_debug_dir(struct mic_device *mdev) @@ -124,7 +124,7 @@ void mic_create_debug_dir(struct mic_device *mdev) &mic_msi_irq_info_fops); } -/** +/* * mic_delete_debug_dir - Uninitialize MIC debugfs entries. */ void mic_delete_debug_dir(struct mic_device *mdev) @@ -132,7 +132,7 @@ 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) @@ -140,7 +140,7 @@ 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) -- cgit v1.2.3-59-g8ed1b From 0cfbe64c56c329b464c378ba8612e684c8e12809 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 1 Jul 2020 09:31:03 +0100 Subject: misc: mic: cosm: cosm_main: Document 'force' function argument Fixes the following W=1 kernel build warning: drivers/misc/mic/cosm/cosm_main.c:31: warning: Function parameter or member 'force' not described in 'cosm_hw_reset' Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20200701083118.45744-16-lee.jones@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/cosm/cosm_main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/misc/mic/cosm/cosm_main.c b/drivers/misc/mic/cosm/cosm_main.c index f9133c4f6105..ebb0eac43754 100644 --- a/drivers/misc/mic/cosm/cosm_main.c +++ b/drivers/misc/mic/cosm/cosm_main.c @@ -26,6 +26,7 @@ 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) { -- cgit v1.2.3-59-g8ed1b From 887d670e29fa7d5e046d2607249ffe56fe1e54bc Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 1 Jul 2020 09:31:04 +0100 Subject: misc: mic: scif: scif_ports: Fix copy 'n' paste error __scif_get_port() has never taken 'port' as an argument since its inception back in 2015. Probably document the proper arguments expected 'start' and 'end'. Fixes the following W=1 kernel build warnings: drivers/misc/mic/scif/scif_ports.c:36: warning: Function parameter or member 'start' not described in '__scif_get_port drivers/misc/mic/scif/scif_ports.c:36: warning: Function parameter or member 'end' not described in '__scif_get_port' drivers/misc/mic/scif/scif_ports.c:36: warning: Excess function parameter 'port' description in '__scif_get_port' Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20200701083118.45744-17-lee.jones@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/scif/scif_ports.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/misc/mic/scif/scif_ports.c b/drivers/misc/mic/scif/scif_ports.c index 547a71285069..4bdb5ef9a139 100644 --- a/drivers/misc/mic/scif/scif_ports.c +++ b/drivers/misc/mic/scif/scif_ports.c @@ -14,11 +14,11 @@ 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. + * @ref_cnt: Reference count since there can be multiple endpoints + * created via scif_accept(..) simultaneously using a port. */ struct scif_port { int ref_cnt; @@ -27,7 +27,8 @@ struct scif_port { /** * __scif_get_port - Reserve a specified port # for SCIF and add it * to the global list. - * @port : port # to be reserved. + * @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. -- cgit v1.2.3-59-g8ed1b From a761b8b00c6cde8f20cdcafdfba685118f2832e3 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 1 Jul 2020 09:31:05 +0100 Subject: misc: mic: cosm: cosm_debugfs: Demote function headers from kerneldoc The correct format is not used and no attempt has been made to document the function arguments. Makes sense to just demote the header back down to a simple comment. Fixes the following W=1 warnings: drivers/misc/mic/cosm/cosm_debugfs.c:25: warning: Function parameter or member 's' not described in 'log_buf_show' drivers/misc/mic/cosm/cosm_debugfs.c:25: warning: Function parameter or member 'unused' not described in 'log_buf_show' drivers/misc/mic/cosm/cosm_debugfs.c:78: warning: Function parameter or member 's' not described in 'force_reset_show' drivers/misc/mic/cosm/cosm_debugfs.c:78: warning: Function parameter or member 'pos' not described in 'force_reset_show' Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20200701083118.45744-18-lee.jones@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/cosm/cosm_debugfs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/misc/mic/cosm/cosm_debugfs.c b/drivers/misc/mic/cosm/cosm_debugfs.c index 68a731fd86de..cb55653cf1f9 100644 --- a/drivers/misc/mic/cosm/cosm_debugfs.c +++ b/drivers/misc/mic/cosm/cosm_debugfs.c @@ -15,7 +15,7 @@ /* 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 @@ -68,7 +68,7 @@ done: DEFINE_SHOW_ATTRIBUTE(log_buf); -/** +/* * force_reset_show - Force MIC reset * * Invokes the force_reset COSM bus op instead of the standard reset -- cgit v1.2.3-59-g8ed1b From cf2a06c133ddb07d90af78569f5d3e0551cfde7f Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 1 Jul 2020 09:31:06 +0100 Subject: misc: mic: scif: scif_api: Remove set but unused variable 'read_size' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 'read_size' appears to have been assigned, but never used since the SCIF messaging and node enumeration APIs were introduced in 2015. Makes sense to simply remove it. Fixes the following W=1 warning: drivers/misc/mic/scif/scif_api.c: In function ‘_scif_recv’: drivers/misc/mic/scif/scif_api.c:1000:6: warning: variable ‘read_size’ set but not used [-Wunused-but-set-variable] 1000 | int read_size; | ^~~~~~~~~ Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20200701083118.45744-19-lee.jones@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/scif/scif_api.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/misc/mic/scif/scif_api.c b/drivers/misc/mic/scif/scif_api.c index 781217c030a6..d7faffdd87e9 100644 --- a/drivers/misc/mic/scif/scif_api.c +++ b/drivers/misc/mic/scif/scif_api.c @@ -997,7 +997,6 @@ static int _scif_send(scif_epd_t epd, void *msg, int len, int flags) static int _scif_recv(scif_epd_t epd, void *msg, int len, int flags) { - int read_size; struct scif_endpt *ep = (struct scif_endpt *)epd; struct scifmsg notif_msg; int curr_recv_len = 0, remaining_len = len, read_count; @@ -1017,8 +1016,7 @@ static int _scif_recv(scif_epd_t epd, void *msg, int len, int flags) * important for the Non Blocking case. */ curr_recv_len = min(remaining_len, read_count); - read_size = scif_rb_get_next(&qp->inbound_q, - msg, curr_recv_len); + scif_rb_get_next(&qp->inbound_q, msg, curr_recv_len); if (ep->state == SCIFEP_CONNECTED) { /* * Update the read pointer only if the endpoint -- cgit v1.2.3-59-g8ed1b From 5d137a360ac5fc02a4172b823a4293067334553e Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 1 Jul 2020 09:31:07 +0100 Subject: misc: mic: scif: scif_api: Demote scif_accept() function header The correct format is not used and no attempt has been made to document the function arguments. Makes sense to just demote the header back down to a simple comment. Fixes the following W=1 warnings: drivers/misc/mic/scif/scif_api.c:739: warning: Function parameter or member 'epd' not described in 'scif_accept' drivers/misc/mic/scif/scif_api.c:739: warning: Function parameter or member 'peer' not described in 'scif_accept' drivers/misc/mic/scif/scif_api.c:739: warning: Function parameter or member 'newepd' not described in 'scif_accept' drivers/misc/mic/scif/scif_api.c:739: warning: Function parameter or member 'flags' not described in 'scif_accept' Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20200701083118.45744-20-lee.jones@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/scif/scif_api.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/mic/scif/scif_api.c b/drivers/misc/mic/scif/scif_api.c index d7faffdd87e9..9cc6b2a6cf22 100644 --- a/drivers/misc/mic/scif/scif_api.c +++ b/drivers/misc/mic/scif/scif_api.c @@ -713,7 +713,7 @@ int scif_connect(scif_epd_t epd, struct scif_port_id *dst) } 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 -- cgit v1.2.3-59-g8ed1b From 3e30b9e3b30ef60e2302ee36466bb738eb088729 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 1 Jul 2020 09:31:08 +0100 Subject: misc: mic: scif: scif_epd: Describe missing 'scifdev' argument(s) For some reason (copy 'n' paste error?) the passed in pointers to 'struct scif_dev scifdev' weren't described throughout the source file. Change that. Fixes the following W=1 kernel build warning(s): drivers/misc/mic/scif/scif_epd.c:120: warning: Function parameter or member 'scifdev' not described in 'scif_cnctreq' drivers/misc/mic/scif/scif_epd.c:166: warning: Function parameter or member 'scifdev' not described in 'scif_cnctgnt' drivers/misc/mic/scif/scif_epd.c:191: warning: Function parameter or member 'scifdev' not described in 'scif_cnctgnt_ack' drivers/misc/mic/scif/scif_epd.c:213: warning: Function parameter or member 'scifdev' not described in 'scif_cnctgnt_nack' drivers/misc/mic/scif/scif_epd.c:230: warning: Function parameter or member 'scifdev' not described in 'scif_cnctrej' drivers/misc/mic/scif/scif_epd.c:255: warning: Function parameter or member 'scifdev' not described in 'scif_discnct' drivers/misc/mic/scif/scif_epd.c:309: warning: Function parameter or member 'scifdev' not described in 'scif_discnt_ack' drivers/misc/mic/scif/scif_epd.c:325: warning: Function parameter or member 'scifdev' not described in 'scif_clientsend' drivers/misc/mic/scif/scif_epd.c:341: warning: Function parameter or member 'scifdev' not described in 'scif_clientrcvd' Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20200701083118.45744-21-lee.jones@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/scif/scif_epd.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/misc/mic/scif/scif_epd.c b/drivers/misc/mic/scif/scif_epd.c index 590baca9dc7b..426687f6696b 100644 --- a/drivers/misc/mic/scif/scif_epd.c +++ b/drivers/misc/mic/scif/scif_epd.c @@ -104,6 +104,7 @@ void scif_cleanup_zombie_epd(void) /** * 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 @@ -155,6 +156,7 @@ conreq_sendrej: /** * 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 @@ -181,6 +183,7 @@ void scif_cnctgnt(struct scif_dev *scifdev, struct scifmsg *msg) /** * 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. @@ -203,6 +206,7 @@ void scif_cnctgnt_ack(struct scif_dev *scifdev, struct scifmsg *msg) /** * 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. @@ -221,6 +225,7 @@ void scif_cnctgnt_nack(struct scif_dev *scifdev, struct scifmsg *msg) /** * 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 @@ -240,6 +245,7 @@ void scif_cnctrej(struct scif_dev *scifdev, struct scifmsg *msg) /** * 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 @@ -301,6 +307,7 @@ discnct_ack: /** * 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 @@ -317,6 +324,7 @@ void scif_discnt_ack(struct scif_dev *scifdev, struct scifmsg *msg) /** * 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. @@ -333,6 +341,7 @@ void scif_clientsend(struct scif_dev *scifdev, struct scifmsg *msg) /** * 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. -- cgit v1.2.3-59-g8ed1b From 5ab3f590c738a193e9d3edd6bc56f50e19fe7eb9 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 1 Jul 2020 09:31:09 +0100 Subject: misc: mic: scif: scif_nodeqp: Fix a bunch of kerneldoc issues Includes; missing function argument documentation, misnamed argument tags, the promotion of obvious kerneldoc header candidates and an ordering issue (functions must follow the kerneldoc which describes them). Fixes the following W=1 kernel build issues: drivers/misc/mic/scif/scif_nodeqp.c:452: warning: Function parameter or member 'scifdev' not described in 'scif_node_connect' drivers/misc/mic/scif/scif_nodeqp.c:730: warning: Function parameter or member 'unused' not described in 'scif_exit' drivers/misc/mic/scif/scif_nodeqp.c:730: warning: Excess function parameter 'msg' description in 'scif_exit' drivers/misc/mic/scif/scif_nodeqp.c:748: warning: Function parameter or member 'unused' not described in 'scif_exit_ack' drivers/misc/mic/scif/scif_nodeqp.c:748: warning: Excess function parameter 'msg' description in 'scif_exit_ack' drivers/misc/mic/scif/scif_nodeqp.c:939: warning: Function parameter or member 'scifdev' not described in 'scif_node_add_nack' drivers/misc/mic/scif/scif_nodeqp.c:988: warning: Function parameter or member 'scifdev' not described in 'scif_get_node_info_resp' drivers/misc/mic/scif/scif_nodeqp.c:1070: warning: cannot understand function prototype: 'int scif_max_msg_id = SCIF_MAX_MSG; ' drivers/misc/mic/scif/scif_nodeqp.c:1129: warning: Function parameter or member 'unused' not described in 'scif_loopb_wq_handler' drivers/misc/mic/scif/scif_nodeqp.c:1129: warning: Excess function parameter 'work' description in 'scif_loopb_wq_handler' Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20200701083118.45744-22-lee.jones@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/scif/scif_nodeqp.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/misc/mic/scif/scif_nodeqp.c b/drivers/misc/mic/scif/scif_nodeqp.c index ea084626fe11..e0748be373f1 100644 --- a/drivers/misc/mic/scif/scif_nodeqp.c +++ b/drivers/misc/mic/scif/scif_nodeqp.c @@ -443,6 +443,7 @@ static void scif_deinit_p2p_info(struct scif_dev *scifdev, /** * 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 @@ -719,7 +720,7 @@ scif_init(struct scif_dev *scifdev, struct scifmsg *msg) /** * scif_exit() - Respond to SCIF_EXIT interrupt message * @scifdev: Remote SCIF device node - * @msg: Interrupt message + * @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 @@ -740,7 +741,7 @@ scif_exit(struct scif_dev *scifdev, struct scifmsg *unused) /** * scif_exitack() - Respond to SCIF_EXIT_ACK interrupt message * @scifdev: Remote SCIF device node - * @msg: Interrupt message + * @unused: Interrupt message (unused) * */ static __always_inline void @@ -930,6 +931,7 @@ local_error: /** * 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. @@ -946,8 +948,9 @@ scif_node_add_nack(struct scif_dev *scifdev, struct scifmsg *msg) } } -/* +/** * scif_node_remove: Handle SCIF_NODE_REMOVE message + * @scifdev: Remote SCIF device node * @msg: Interrupt message * * Handle node removal. @@ -962,8 +965,9 @@ scif_node_remove(struct scif_dev *scifdev, struct scifmsg *msg) 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. @@ -979,6 +983,7 @@ scif_node_remove_ack(struct scif_dev *scifdev, struct scifmsg *msg) /** * 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. @@ -1058,6 +1063,7 @@ static void (*scif_intr_func[SCIF_MAX_MSG + 1]) 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 @@ -1067,8 +1073,6 @@ static void (*scif_intr_func[SCIF_MAX_MSG + 1]) * This routine calls the appropriate routine to handle a Node Qp * message receipt */ -static int scif_max_msg_id = SCIF_MAX_MSG; - static void scif_nodeqp_msg_handler(struct scif_dev *scifdev, struct scif_qp *qp, struct scifmsg *msg) @@ -1117,7 +1121,7 @@ void scif_nodeqp_intrhandler(struct scif_dev *scifdev, struct scif_qp *qp) /** * scif_loopb_wq_handler - Loopback Workqueue Handler. - * @work: loop back work + * @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 -- cgit v1.2.3-59-g8ed1b From 9afe1839e878a0745f8f325628ea445c47e689ab Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 1 Jul 2020 09:31:10 +0100 Subject: misc: mic: scif: scif_nm: Supply various kerneldoc fix-ups Includes; missing descriptions for function args, the promotion of obvious kerneldoc headers and the mis-formatting of existing argument descriptions. Fixes the following W=1 kernel build warnings: drivers/misc/mic/scif/scif_nm.c:19: warning: Function parameter or member 'node' not described in 'scif_invalidate_ep' drivers/misc/mic/scif/scif_nm.c:173: warning: Function parameter or member 'node_id' not described in 'scif_disconnect_node' Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20200701083118.45744-23-lee.jones@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/scif/scif_nm.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/misc/mic/scif/scif_nm.c b/drivers/misc/mic/scif/scif_nm.c index c537df84539a..c4d9422082b7 100644 --- a/drivers/misc/mic/scif/scif_nm.c +++ b/drivers/misc/mic/scif/scif_nm.c @@ -14,6 +14,8 @@ /** * 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) { @@ -99,11 +101,10 @@ void scif_send_acks(struct scif_dev *dev) } } -/* - * scif_cleanup_scifdev - * +/** + * scif_cleanup_scifdev - Uninitialize SCIF data structures for remote + * SCIF device. * @dev: Remote SCIF device. - * Uninitialize SCIF data structures for remote SCIF device. */ void scif_cleanup_scifdev(struct scif_dev *dev) { @@ -136,8 +137,8 @@ void scif_cleanup_scifdev(struct scif_dev *dev) scif_cleanup_qp(dev); } -/* - * scif_remove_node: +/** + * scif_remove_node * * @node: Node to remove */ @@ -162,9 +163,9 @@ static int scif_send_rmnode_msg(int node, int remove_node) } /** - * scif_node_disconnect: + * scif_node_disconnect * - * @node_id[in]: source node id. + * @node_id: source node id [in] * @mgmt_initiated: Disconnection initiated from the mgmt node * * Disconnect a node from the scif network. -- cgit v1.2.3-59-g8ed1b From 31c68981c53acd40a1e5f4b3afebf1a2b924bad5 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 1 Jul 2020 09:31:11 +0100 Subject: misc: mic: scif: scif_dma: Fix a couple of kerneldoc issues Firstly demote scif_rma_destroy_tcw()'s header to a standard comment as no attempt has been made to document any of the function arguments. Secondly, provide missing description for 'prot' for scif_register_temp(). Fixes the following W=1 kernel build warnings: drivers/misc/mic/scif/scif_dma.c:110: warning: Function parameter or member 'mmn' not described in '__scif_rma_destroy_tcw' drivers/misc/mic/scif/scif_dma.c:110: warning: Function parameter or member 'start' not described in '__scif_rma_destroy_tcw' drivers/misc/mic/scif/scif_dma.c:110: warning: Function parameter or member 'len' not described in '__scif_rma_destroy_tcw' drivers/misc/mic/scif/scif_dma.c:344: warning: Function parameter or member 'prot' not described in 'scif_register_temp' Cc: Sudeep Dutt Cc: Ashutosh Dixit Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20200701083118.45744-24-lee.jones@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/scif/scif_dma.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/misc/mic/scif/scif_dma.c b/drivers/misc/mic/scif/scif_dma.c index c7c873409184..401b98e5ad79 100644 --- a/drivers/misc/mic/scif/scif_dma.c +++ b/drivers/misc/mic/scif/scif_dma.c @@ -99,7 +99,7 @@ int scif_reserve_dma_chan(struct scif_endpt *ep) } #ifdef CONFIG_MMU_NOTIFIER -/** +/* * scif_rma_destroy_tcw: * * This routine destroys temporary cached windows @@ -332,6 +332,7 @@ static bool scif_rma_tc_can_cache(struct scif_endpt *ep, size_t cur_bytes) * @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. * -- cgit v1.2.3-59-g8ed1b From bb11b4eb532be83e97a891da47ead7c4f1f83b94 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 1 Jul 2020 09:31:12 +0100 Subject: misc: mic: scif: scif_fence: Fix a bunch of different documentation issues Firstly add missing descriptions for every use of 'scifdev', then correct incorrect formatting of existing function argument descriptions changing to the expected format of '@.*: ', finally add missing description for function argument 'mark'. Fixes the following W=1 warnings: drivers/misc/mic/scif/scif_fence.c:19: warning: Function parameter or member 'scifdev' not described in 'scif_recv_mark' drivers/misc/mic/scif/scif_fence.c:41: warning: Function parameter or member 'scifdev' not described in 'scif_recv_mark_resp' drivers/misc/mic/scif/scif_fence.c:64: warning: Function parameter or member 'scifdev' not described in 'scif_recv_wait' drivers/misc/mic/scif/scif_fence.c:101: warning: Function parameter or member 'scifdev' not described in 'scif_recv_wait_resp' drivers/misc/mic/scif/scif_fence.c:122: warning: Function parameter or member 'scifdev' not described in 'scif_recv_sig_local' drivers/misc/mic/scif/scif_fence.c:143: warning: Function parameter or member 'scifdev' not described in 'scif_recv_sig_remote' drivers/misc/mic/scif/scif_fence.c:164: warning: Function parameter or member 'scifdev' not described in 'scif_recv_sig_resp' drivers/misc/mic/scif/scif_fence.c:515: warning: Function parameter or member 'epd' not described in 'scif_send_fence_signal' drivers/misc/mic/scif/scif_fence.c:515: warning: Function parameter or member 'roff' not described in 'scif_send_fence_signal' drivers/misc/mic/scif/scif_fence.c:515: warning: Function parameter or member 'rval' not described in 'scif_send_fence_signal' drivers/misc/mic/scif/scif_fence.c:515: warning: Function parameter or member 'loff' not described in 'scif_send_fence_signal' drivers/misc/mic/scif/scif_fence.c:515: warning: Function parameter or member 'lval' not described in 'scif_send_fence_signal' drivers/misc/mic/scif/scif_fence.c:515: warning: Function parameter or member 'flags' not described in 'scif_send_fence_signal' Cc: Sudeep Dutt Cc: Ashutosh Dixit Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20200701083118.45744-25-lee.jones@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/scif/scif_fence.c | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/drivers/misc/mic/scif/scif_fence.c b/drivers/misc/mic/scif/scif_fence.c index 657fd4a20656..4fedf6183951 100644 --- a/drivers/misc/mic/scif/scif_fence.c +++ b/drivers/misc/mic/scif/scif_fence.c @@ -11,6 +11,7 @@ /** * scif_recv_mark: Handle SCIF_MARK request + * @scifdev: SCIF device * @msg: Interrupt message * * The peer has requested a mark. @@ -33,6 +34,7 @@ void scif_recv_mark(struct scif_dev *scifdev, struct scifmsg *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. @@ -56,6 +58,7 @@ void scif_recv_mark_resp(struct scif_dev *scifdev, struct scifmsg *msg) /** * scif_recv_wait: Handle SCIF_WAIT request + * @scifdev: SCIF device * @msg: Interrupt message * * The peer has requested waiting on a fence. @@ -93,6 +96,7 @@ void scif_recv_wait(struct scif_dev *scifdev, struct scifmsg *msg) /** * 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. @@ -114,6 +118,7 @@ void scif_recv_wait_resp(struct scif_dev *scifdev, struct scifmsg *msg) /** * 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. @@ -135,6 +140,7 @@ void scif_recv_sig_local(struct scif_dev *scifdev, struct scifmsg *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. @@ -156,6 +162,7 @@ void scif_recv_sig_remote(struct scif_dev *scifdev, struct scifmsg *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. @@ -280,12 +287,12 @@ 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. + * @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. @@ -501,12 +508,12 @@ retry: /** * 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 + * @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 */ @@ -577,10 +584,11 @@ static void scif_fence_mark_cb(void *arg) atomic_dec(&ep->rma_info.fence_refcount); } -/* +/** * _scif_fence_mark: + * @epd: endpoint descriptor + * @mark: DMA mark to set-up * - * @epd - endpoint descriptor * Set up a mark for this endpoint and return the value of the mark. */ int _scif_fence_mark(scif_epd_t epd, int *mark) -- cgit v1.2.3-59-g8ed1b From ff512a8b79320f4893403f812ae2838226cade32 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 1 Jul 2020 09:31:13 +0100 Subject: misc: mic: scif: scif_rma: Repair a bunch of kerneldoc issues Firstly, for some reason documentation of all 'scifdev' arguments are missing, so apply those. Also remove 2 descriptions of 'ep' which appear to document non-existent function arguments. drivers/misc/mic/scif/scif_rma.c:468: warning: Function parameter or member 'scifdev' not described in 'scif_create_remote_window' drivers/misc/mic/scif/scif_rma.c:468: warning: Excess function parameter 'ep' description in 'scif_create_remote_window' drivers/misc/mic/scif/scif_rma.c:510: warning: Excess function parameter 'ep' description in 'scif_destroy_remote_window' drivers/misc/mic/scif/scif_rma.c:1045: warning: Function parameter or member 'scifdev' not described in 'scif_alloc_req' drivers/misc/mic/scif/scif_rma.c:1080: warning: Function parameter or member 'scifdev' not described in 'scif_alloc_gnt_rej' drivers/misc/mic/scif/scif_rma.c:1104: warning: Function parameter or member 'scifdev' not described in 'scif_free_virt' drivers/misc/mic/scif/scif_rma.c:1142: warning: Function parameter or member 'scifdev' not described in 'scif_recv_reg' drivers/misc/mic/scif/scif_rma.c:1178: warning: Function parameter or member 'scifdev' not described in 'scif_recv_unreg' drivers/misc/mic/scif/scif_rma.c:1243: warning: Function parameter or member 'scifdev' not described in 'scif_recv_reg_ack' drivers/misc/mic/scif/scif_rma.c:1262: warning: Function parameter or member 'scifdev' not described in 'scif_recv_reg_nack' drivers/misc/mic/scif/scif_rma.c:1280: warning: Function parameter or member 'scifdev' not described in 'scif_recv_unreg_ack' drivers/misc/mic/scif/scif_rma.c:1299: warning: Function parameter or member 'scifdev' not described in 'scif_recv_unreg_nack' Cc: Sudeep Dutt Cc: Ashutosh Dixit Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20200701083118.45744-26-lee.jones@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/scif/scif_rma.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/misc/mic/scif/scif_rma.c b/drivers/misc/mic/scif/scif_rma.c index 406cd5abfa72..de8f61efaef5 100644 --- a/drivers/misc/mic/scif/scif_rma.c +++ b/drivers/misc/mic/scif/scif_rma.c @@ -458,7 +458,7 @@ static void scif_destroy_remote_lookup(struct scif_dev *remote_dev, /** * scif_create_remote_window: - * @ep: end point + * @scifdev: SCIF device * @nr_pages: number of pages in window * * Allocate and prepare a remote registration window. @@ -500,7 +500,6 @@ error_ret: /** * scif_destroy_remote_window: - * @ep: end point * @window: remote registration window * * Deallocate resources for remote window. @@ -1037,6 +1036,7 @@ void scif_free_window_offset(struct scif_endpt *ep, /** * scif_alloc_req: Respond to SCIF_ALLOC_REQ interrupt message + * @scifdev: SCIF device * @msg: Interrupt message * * Remote side is requesting a memory allocation. @@ -1072,6 +1072,7 @@ error: /** * 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. @@ -1096,6 +1097,7 @@ void scif_alloc_gnt_rej(struct scif_dev *scifdev, struct scifmsg *msg) /** * scif_free_virt: Respond to SCIF_FREE_VIRT interrupt message + * @scifdev: SCIF device * @msg: Interrupt message * * Free up memory kmalloc'd earlier. @@ -1134,6 +1136,7 @@ scif_fixup_aper_base(struct scif_dev *dev, struct scif_window *window) /** * scif_recv_reg: Respond to SCIF_REGISTER interrupt message + * @scifdev: SCIF device * @msg: Interrupt message * * Update remote window list with a new registered window. @@ -1170,6 +1173,7 @@ void scif_recv_reg(struct scif_dev *scifdev, struct scifmsg *msg) /** * scif_recv_unreg: Respond to SCIF_UNREGISTER interrupt message + * @scifdev: SCIF device * @msg: Interrupt message * * Remove window from remote registration list; @@ -1235,6 +1239,7 @@ error: /** * 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. @@ -1253,6 +1258,7 @@ void scif_recv_reg_ack(struct scif_dev *scifdev, struct scifmsg *msg) /** * 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 @@ -1272,6 +1278,7 @@ void scif_recv_reg_nack(struct scif_dev *scifdev, struct scifmsg *msg) /** * 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. @@ -1290,6 +1297,7 @@ void scif_recv_unreg_ack(struct scif_dev *scifdev, struct scifmsg *msg) /** * 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 -- cgit v1.2.3-59-g8ed1b From b865c5b3220391ca6b4a0948f91f496242a2e04f Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 1 Jul 2020 09:31:15 +0100 Subject: misc: cxl: hcalls: Demote half-assed kerneldoc attempt Function headers will need a lot of work before they reach the standards expected of kerneldoc. Demote them down to basic comments/headers, for now at least. Fixes the following W=1 kernel build warnings: drivers/misc/cxl/hcalls.c:175: warning: Function parameter or member 'unit_address' not described in 'cxl_h_detach_process' drivers/misc/cxl/hcalls.c:175: warning: Function parameter or member 'process_token' not described in 'cxl_h_detach_process' drivers/misc/cxl/hcalls.c:207: warning: Function parameter or member 'unit_address' not described in 'cxl_h_control_function' drivers/misc/cxl/hcalls.c:207: warning: Function parameter or member 'op' not described in 'cxl_h_control_function' drivers/misc/cxl/hcalls.c:207: warning: Function parameter or member 'p1' not described in 'cxl_h_control_function' drivers/misc/cxl/hcalls.c:207: warning: Function parameter or member 'p2' not described in 'cxl_h_control_function' drivers/misc/cxl/hcalls.c:207: warning: Function parameter or member 'p3' not described in 'cxl_h_control_function' drivers/misc/cxl/hcalls.c:207: warning: Function parameter or member 'p4' not described in 'cxl_h_control_function' drivers/misc/cxl/hcalls.c:207: warning: Function parameter or member 'out' not described in 'cxl_h_control_function' drivers/misc/cxl/hcalls.c:245: warning: Function parameter or member 'unit_address' not described in 'cxl_h_reset_afu' drivers/misc/cxl/hcalls.c:258: warning: Function parameter or member 'unit_address' not described in 'cxl_h_suspend_process' drivers/misc/cxl/hcalls.c:258: warning: Function parameter or member 'process_token' not described in 'cxl_h_suspend_process' drivers/misc/cxl/hcalls.c:271: warning: Function parameter or member 'unit_address' not described in 'cxl_h_resume_process' drivers/misc/cxl/hcalls.c:271: warning: Function parameter or member 'process_token' not described in 'cxl_h_resume_process' drivers/misc/cxl/hcalls.c:284: warning: Function parameter or member 'unit_address' not described in 'cxl_h_read_error_state' drivers/misc/cxl/hcalls.c:284: warning: Function parameter or member 'state' not described in 'cxl_h_read_error_state' drivers/misc/cxl/hcalls.c:300: warning: Function parameter or member 'unit_address' not described in 'cxl_h_get_afu_err' drivers/misc/cxl/hcalls.c:300: warning: Function parameter or member 'offset' not described in 'cxl_h_get_afu_err' drivers/misc/cxl/hcalls.c:300: warning: Function parameter or member 'buf_address' not described in 'cxl_h_get_afu_err' drivers/misc/cxl/hcalls.c:300: warning: Function parameter or member 'len' not described in 'cxl_h_get_afu_err' drivers/misc/cxl/hcalls.c:320: warning: Function parameter or member 'unit_address' not described in 'cxl_h_get_config' drivers/misc/cxl/hcalls.c:320: warning: Function parameter or member 'cr_num' not described in 'cxl_h_get_config' drivers/misc/cxl/hcalls.c:320: warning: Function parameter or member 'offset' not described in 'cxl_h_get_config' drivers/misc/cxl/hcalls.c:320: warning: Function parameter or member 'buf_address' not described in 'cxl_h_get_config' drivers/misc/cxl/hcalls.c:320: warning: Function parameter or member 'len' not described in 'cxl_h_get_config' drivers/misc/cxl/hcalls.c:333: warning: Function parameter or member 'unit_address' not described in 'cxl_h_terminate_process' drivers/misc/cxl/hcalls.c:333: warning: Function parameter or member 'process_token' not described in 'cxl_h_terminate_process' drivers/misc/cxl/hcalls.c:351: warning: Function parameter or member 'unit_address' not described in 'cxl_h_collect_vpd' drivers/misc/cxl/hcalls.c:351: warning: Function parameter or member 'record' not described in 'cxl_h_collect_vpd' drivers/misc/cxl/hcalls.c:351: warning: Function parameter or member 'list_address' not described in 'cxl_h_collect_vpd' drivers/misc/cxl/hcalls.c:351: warning: Function parameter or member 'num' not described in 'cxl_h_collect_vpd' drivers/misc/cxl/hcalls.c:351: warning: Function parameter or member 'out' not described in 'cxl_h_collect_vpd' drivers/misc/cxl/hcalls.c:362: warning: Function parameter or member 'unit_address' not described in 'cxl_h_get_fn_error_interrupt' drivers/misc/cxl/hcalls.c:362: warning: Function parameter or member 'reg' not described in 'cxl_h_get_fn_error_interrupt' drivers/misc/cxl/hcalls.c:374: warning: Function parameter or member 'unit_address' not described in 'cxl_h_ack_fn_error_interrupt' drivers/misc/cxl/hcalls.c:374: warning: Function parameter or member 'value' not described in 'cxl_h_ack_fn_error_interrupt' drivers/misc/cxl/hcalls.c:386: warning: Function parameter or member 'unit_address' not described in 'cxl_h_get_error_log' drivers/misc/cxl/hcalls.c:386: warning: Function parameter or member 'value' not described in 'cxl_h_get_error_log' drivers/misc/cxl/hcalls.c:399: warning: Function parameter or member 'unit_address' not described in 'cxl_h_collect_int_info' drivers/misc/cxl/hcalls.c:399: warning: Function parameter or member 'process_token' not described in 'cxl_h_collect_int_info' drivers/misc/cxl/hcalls.c:399: warning: Function parameter or member 'info' not described in 'cxl_h_collect_int_info' drivers/misc/cxl/hcalls.c:440: warning: Function parameter or member 'unit_address' not described in 'cxl_h_control_faults' drivers/misc/cxl/hcalls.c:440: warning: Function parameter or member 'process_token' not described in 'cxl_h_control_faults' drivers/misc/cxl/hcalls.c:440: warning: Function parameter or member 'control_mask' not described in 'cxl_h_control_faults' drivers/misc/cxl/hcalls.c:440: warning: Function parameter or member 'reset_mask' not described in 'cxl_h_control_faults' drivers/misc/cxl/hcalls.c:480: warning: Function parameter or member 'unit_address' not described in 'cxl_h_control_facility' drivers/misc/cxl/hcalls.c:480: warning: Function parameter or member 'op' not described in 'cxl_h_control_facility' drivers/misc/cxl/hcalls.c:480: warning: Function parameter or member 'p1' not described in 'cxl_h_control_facility' drivers/misc/cxl/hcalls.c:480: warning: Function parameter or member 'p2' not described in 'cxl_h_control_facility' drivers/misc/cxl/hcalls.c:480: warning: Function parameter or member 'p3' not described in 'cxl_h_control_facility' drivers/misc/cxl/hcalls.c:480: warning: Function parameter or member 'p4' not described in 'cxl_h_control_facility' drivers/misc/cxl/hcalls.c:480: warning: Function parameter or member 'out' not described in 'cxl_h_control_facility' drivers/misc/cxl/hcalls.c:516: warning: Function parameter or member 'unit_address' not described in 'cxl_h_reset_adapter' drivers/misc/cxl/hcalls.c:532: warning: Function parameter or member 'unit_address' not described in 'cxl_h_collect_vpd_adapter' drivers/misc/cxl/hcalls.c:532: warning: Function parameter or member 'list_address' not described in 'cxl_h_collect_vpd_adapter' drivers/misc/cxl/hcalls.c:532: warning: Function parameter or member 'num' not described in 'cxl_h_collect_vpd_adapter' drivers/misc/cxl/hcalls.c:532: warning: Function parameter or member 'out' not described in 'cxl_h_collect_vpd_adapter' drivers/misc/cxl/hcalls.c:561: warning: Function parameter or member 'unit_address' not described in 'cxl_h_download_facility' drivers/misc/cxl/hcalls.c:561: warning: Function parameter or member 'op' not described in 'cxl_h_download_facility' drivers/misc/cxl/hcalls.c:561: warning: Function parameter or member 'list_address' not described in 'cxl_h_download_facility' drivers/misc/cxl/hcalls.c:561: warning: Function parameter or member 'num' not described in 'cxl_h_download_facility' drivers/misc/cxl/hcalls.c:561: warning: Function parameter or member 'out' not described in 'cxl_h_download_facility' drivers/misc/cxl/hcalls.c:626: warning: Function parameter or member 'unit_address' not described in 'cxl_h_download_adapter_image' drivers/misc/cxl/hcalls.c:626: warning: Function parameter or member 'list_address' not described in 'cxl_h_download_adapter_image' drivers/misc/cxl/hcalls.c:626: warning: Function parameter or member 'num' not described in 'cxl_h_download_adapter_image' drivers/misc/cxl/hcalls.c:626: warning: Function parameter or member 'out' not described in 'cxl_h_download_adapter_image' drivers/misc/cxl/hcalls.c:639: warning: Function parameter or member 'unit_address' not described in 'cxl_h_validate_adapter_image' drivers/misc/cxl/hcalls.c:639: warning: Function parameter or member 'list_address' not described in 'cxl_h_validate_adapter_image' drivers/misc/cxl/hcalls.c:639: warning: Function parameter or member 'num' not described in 'cxl_h_validate_adapter_image' Cc: Frederic Barrat Cc: Andrew Donnellan Cc: linuxppc-dev@lists.ozlabs.org Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20200701083118.45744-28-lee.jones@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/cxl/hcalls.c | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/drivers/misc/cxl/hcalls.c b/drivers/misc/cxl/hcalls.c index b7c57f67f4f5..aba5e20eeb1f 100644 --- a/drivers/misc/cxl/hcalls.c +++ b/drivers/misc/cxl/hcalls.c @@ -167,7 +167,7 @@ long cxl_h_attach_process(u64 unit_address, } } -/** +/* * cxl_h_detach_process - Detach a process element from a coherent * platform function. */ @@ -197,7 +197,7 @@ long cxl_h_detach_process(u64 unit_address, u64 process_token) } } -/** +/* * cxl_h_control_function - This H_CONTROL_CA_FUNCTION hypervisor call allows * the partition to manipulate or query * certain coherent platform function behaviors. @@ -238,7 +238,7 @@ static long cxl_h_control_function(u64 unit_address, u64 op, } } -/** +/* * cxl_h_reset_afu - Perform a reset to the coherent platform function. */ long cxl_h_reset_afu(u64 unit_address) @@ -249,7 +249,7 @@ long cxl_h_reset_afu(u64 unit_address) NULL); } -/** +/* * cxl_h_suspend_process - Suspend a process from being executed * Parameter1 = process-token as returned from H_ATTACH_CA_PROCESS when * process was attached. @@ -262,7 +262,7 @@ long cxl_h_suspend_process(u64 unit_address, u64 process_token) NULL); } -/** +/* * cxl_h_resume_process - Resume a process to be executed * Parameter1 = process-token as returned from H_ATTACH_CA_PROCESS when * process was attached. @@ -275,7 +275,7 @@ long cxl_h_resume_process(u64 unit_address, u64 process_token) NULL); } -/** +/* * cxl_h_read_error_state - Checks the error state of the coherent * platform function. * R4 contains the error state @@ -288,7 +288,7 @@ long cxl_h_read_error_state(u64 unit_address, u64 *state) state); } -/** +/* * cxl_h_get_afu_err - collect the AFU error buffer * Parameter1 = byte offset into error buffer to retrieve, valid values * are between 0 and (ibm,error-buffer-size - 1) @@ -304,7 +304,7 @@ long cxl_h_get_afu_err(u64 unit_address, u64 offset, NULL); } -/** +/* * cxl_h_get_config - collect configuration record for the * coherent platform function * Parameter1 = # of configuration record to retrieve, valid values are @@ -324,7 +324,7 @@ long cxl_h_get_config(u64 unit_address, u64 cr_num, u64 offset, NULL); } -/** +/* * cxl_h_terminate_process - Terminate the process before completion * Parameter1 = process-token as returned from H_ATTACH_CA_PROCESS when * process was attached. @@ -337,7 +337,7 @@ long cxl_h_terminate_process(u64 unit_address, u64 process_token) NULL); } -/** +/* * cxl_h_collect_vpd - Collect VPD for the coherent platform function. * Parameter1 = # of VPD record to retrieve, valid values are between 0 * and (ibm,#config-records - 1). @@ -355,7 +355,7 @@ long cxl_h_collect_vpd(u64 unit_address, u64 record, u64 list_address, out); } -/** +/* * cxl_h_get_fn_error_interrupt - Read the function-wide error data based on an interrupt */ long cxl_h_get_fn_error_interrupt(u64 unit_address, u64 *reg) @@ -365,7 +365,7 @@ long cxl_h_get_fn_error_interrupt(u64 unit_address, u64 *reg) 0, 0, 0, 0, reg); } -/** +/* * cxl_h_ack_fn_error_interrupt - Acknowledge function-wide error data * based on an interrupt * Parameter1 = value to write to the function-wide error interrupt register @@ -378,7 +378,7 @@ long cxl_h_ack_fn_error_interrupt(u64 unit_address, u64 value) NULL); } -/** +/* * cxl_h_get_error_log - Retrieve the Platform Log ID (PLID) of * an error log */ @@ -390,7 +390,7 @@ long cxl_h_get_error_log(u64 unit_address, u64 value) NULL); } -/** +/* * cxl_h_collect_int_info - Collect interrupt info about a coherent * platform function after an interrupt occurred. */ @@ -425,7 +425,7 @@ long cxl_h_collect_int_info(u64 unit_address, u64 process_token, } } -/** +/* * cxl_h_control_faults - Control the operation of a coherent platform * function after a fault occurs. * @@ -470,7 +470,7 @@ long cxl_h_control_faults(u64 unit_address, u64 process_token, } } -/** +/* * cxl_h_control_facility - This H_CONTROL_CA_FACILITY hypervisor call * allows the partition to manipulate or query * certain coherent platform facility behaviors. @@ -509,7 +509,7 @@ static long cxl_h_control_facility(u64 unit_address, u64 op, } } -/** +/* * cxl_h_reset_adapter - Perform a reset to the coherent platform facility. */ long cxl_h_reset_adapter(u64 unit_address) @@ -520,7 +520,7 @@ long cxl_h_reset_adapter(u64 unit_address) NULL); } -/** +/* * cxl_h_collect_vpd - Collect VPD for the coherent platform function. * Parameter1 = 4K naturally aligned real buffer containing block * list entries @@ -536,7 +536,7 @@ long cxl_h_collect_vpd_adapter(u64 unit_address, u64 list_address, out); } -/** +/* * cxl_h_download_facility - This H_DOWNLOAD_CA_FACILITY * hypervisor call provide platform support for * downloading a base adapter image to the coherent @@ -616,7 +616,7 @@ static long cxl_h_download_facility(u64 unit_address, u64 op, } } -/** +/* * cxl_h_download_adapter_image - Download the base image to the coherent * platform facility. */ @@ -629,7 +629,7 @@ long cxl_h_download_adapter_image(u64 unit_address, list_address, num, out); } -/** +/* * cxl_h_validate_adapter_image - Validate the base image in the coherent * platform facility. */ -- cgit v1.2.3-59-g8ed1b From 28fc491e9be6c665f5af79a8ffa9ff85e7d06cb1 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 1 Jul 2020 09:31:16 +0100 Subject: misc: ocxl: config: Provide correct formatting to function headers A nice attempt was made to provide kerneldoc headers for read_template_version() and read_afu_lpc_memory_info() however, the provided formatting does not match what is expected by kerneldoc. Fixes the following W=1 warnings: drivers/misc/ocxl/config.c:286: warning: Function parameter or member 'dev' not described in 'read_template_version' drivers/misc/ocxl/config.c:286: warning: Function parameter or member 'fn' not described in 'read_template_version' drivers/misc/ocxl/config.c:286: warning: Function parameter or member 'len' not described in 'read_template_version' drivers/misc/ocxl/config.c:286: warning: Function parameter or member 'version' not described in 'read_template_version' drivers/misc/ocxl/config.c:489: warning: Function parameter or member 'dev' not described in 'read_afu_lpc_memory_info' drivers/misc/ocxl/config.c:489: warning: Function parameter or member 'fn' not described in 'read_afu_lpc_memory_info' drivers/misc/ocxl/config.c:489: warning: Function parameter or member 'afu' not described in 'read_afu_lpc_memory_info' Cc: Frederic Barrat Cc: Andrew Donnellan Cc: linuxppc-dev@lists.ozlabs.org Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20200701083118.45744-29-lee.jones@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/ocxl/config.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/misc/ocxl/config.c b/drivers/misc/ocxl/config.c index c8e19bfb5ef9..e3b99a39d207 100644 --- a/drivers/misc/ocxl/config.c +++ b/drivers/misc/ocxl/config.c @@ -273,11 +273,11 @@ static int read_afu_info(struct pci_dev *dev, struct ocxl_fn_config *fn, } /** - * Read the template version from the AFU - * dev: the device for the AFU - * fn: the AFU offsets - * len: outputs the template length - * version: outputs the major<<8,minor version + * read_template_version - Read the template version from the AFU + * @dev: the device for the AFU + * @fn: the AFU offsets + * @len: outputs the template length + * @version: outputs the major<<8,minor version * * Returns 0 on success, negative on failure */ @@ -476,10 +476,10 @@ static int validate_afu(struct pci_dev *dev, struct ocxl_afu_config *afu) } /** - * Populate AFU metadata regarding LPC memory - * dev: the device for the AFU - * fn: the AFU offsets - * afu: the AFU struct to populate the LPC metadata into + * read_afu_lpc_memory_info - Populate AFU metadata regarding LPC memory + * @dev: the device for the AFU + * @fn: the AFU offsets + * @afu: the AFU struct to populate the LPC metadata into * * Returns 0 on success, negative on failure */ -- cgit v1.2.3-59-g8ed1b From 182486594a6101abe51629d22a585423ed94a2c8 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 1 Jul 2020 09:31:17 +0100 Subject: misc: ibmvmc: Repair ill-named function argument descriptions Formatting is correct and descriptions are all present, but some of the argument names are not properly represented in the documentation. Fixes the following W=1 build warnings: drivers/misc/ibmvmc.c:780: warning: Function parameter or member 'msg_len' not described in 'ibmvmc_send_msg' drivers/misc/ibmvmc.c:780: warning: Excess function parameter 'msg_length' description in 'ibmvmc_send_msg' drivers/misc/ibmvmc.c:1041: warning: Function parameter or member 'buffer' not described in 'ibmvmc_write' drivers/misc/ibmvmc.c:1041: warning: Excess function parameter 'buf' description in 'ibmvmc_write' drivers/misc/ibmvmc.c:1360: warning: Function parameter or member 'file' not described in 'ibmvmc_ioctl' drivers/misc/ibmvmc.c:1360: warning: Excess function parameter 'session' description in 'ibmvmc_ioctl' Cc: Steven Royer Cc: Adam Reznechek Cc: "Bryant G. Ly" Cc: Dave Engebretsen Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20200701083118.45744-30-lee.jones@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/ibmvmc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/misc/ibmvmc.c b/drivers/misc/ibmvmc.c index 2ed23c99f59f..c0d139c26505 100644 --- a/drivers/misc/ibmvmc.c +++ b/drivers/misc/ibmvmc.c @@ -760,7 +760,7 @@ static int ibmvmc_send_rem_buffer_resp(struct crq_server_adapter *adapter, * @adapter: crq_server_adapter struct * @buffer: ibmvmc_buffer struct * @hmc: ibmvmc_hmc struct - * @msg_length: message length field + * @msg_len: message length field * * This command is sent between the management partition and the hypervisor * in order to signal the arrival of an HMC protocol message. The command @@ -1028,7 +1028,7 @@ static unsigned int ibmvmc_poll(struct file *file, poll_table *wait) * ibmvmc_write - Write * * @file: file struct - * @buf: Character buffer + * @buffer: Character buffer * @count: Count field * @ppos: Offset * @@ -1347,7 +1347,7 @@ static long ibmvmc_ioctl_requestvmc(struct ibmvmc_file_session *session, /** * ibmvmc_ioctl - IOCTL * - * @session: ibmvmc_file_session struct + * @file: file information * @cmd: cmd field * @arg: Argument field * -- cgit v1.2.3-59-g8ed1b From 574ba3667891b432f92478a02f686bb779a1e593 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 1 Jul 2020 16:48:53 +0300 Subject: dt-bindings: phy: zynqmp-psgtr: Fix example's numbers of cells in reg The DT examples are by default compiled in a parent that has #address-cells and #size-cells both set to 1. Fix the example accordingly, even if it doesn't match the actual hardware, as this is the recommended practice for DT bindings examples. Fixes: cea0f76a483d ("dt-bindings: phy: Add DT bindings for Xilinx ZynqMP PSGTR PHY") Signed-off-by: Laurent Pinchart Link: https://lore.kernel.org/r/20200701134853.30656-1-laurent.pinchart@ideasonboard.com Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/phy/xlnx,zynqmp-psgtr.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/phy/xlnx,zynqmp-psgtr.yaml b/Documentation/devicetree/bindings/phy/xlnx,zynqmp-psgtr.yaml index 09e3cde7ebca..04d5654efb38 100644 --- a/Documentation/devicetree/bindings/phy/xlnx,zynqmp-psgtr.yaml +++ b/Documentation/devicetree/bindings/phy/xlnx,zynqmp-psgtr.yaml @@ -94,8 +94,8 @@ examples: - | phy: phy@fd400000 { compatible = "xlnx,zynqmp-psgtr-v1.1"; - reg = <0x0 0xfd400000 0x0 0x40000>, - <0x0 0xfd3d0000 0x0 0x1000>; + reg = <0xfd400000 0x40000>, + <0xfd3d0000 0x1000>; reg-names = "serdes", "siou"; clocks = <&refclks 3>, <&refclks 2>, <&refclks 0>; clock-names = "ref1", "ref2", "ref3"; -- cgit v1.2.3-59-g8ed1b From 1c59c2d95d3160a2af4a8655d19f56648b2dffa3 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Wed, 1 Jul 2020 16:10:17 +0200 Subject: phy: zynqmp: Fix unused-function compiler warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mark xpsgtr_suspend and xpsgtr_resume as __maybe_unused to fix the following compiler warning when building with !CONFIG_PM_SLEEP: drivers/phy/xilinx/phy-zynqmp.c:830:12: warning: ‘xpsgtr_resume’ defined but not used [-Wunused-function] 830 | static int xpsgtr_resume(struct device *dev) | ^~~~~~~~~~~~~ drivers/phy/xilinx/phy-zynqmp.c:819:12: warning: ‘xpsgtr_suspend’ defined but not used [-Wunused-function] 819 | static int xpsgtr_suspend(struct device *dev) | ^~~~~~~~~~~~~~ Also drop the existing #ifdef CONFIG_PM so the functions are always compile-checked regardless of CONFIG_PM and/or CONFIG_PM_SLEEP being set. Reviewed-by: Laurent Pinchart Signed-off-by: Tobias Klauser Link: https://lore.kernel.org/r/20200701141017.26931-1-tklauser@distanz.ch Signed-off-by: Vinod Koul --- drivers/phy/xilinx/phy-zynqmp.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c index 8babee2ce9ec..2b0f921b6ee3 100644 --- a/drivers/phy/xilinx/phy-zynqmp.c +++ b/drivers/phy/xilinx/phy-zynqmp.c @@ -815,8 +815,7 @@ static struct phy *xpsgtr_xlate(struct device *dev, * Power Management */ -#ifdef CONFIG_PM -static int xpsgtr_suspend(struct device *dev) +static int __maybe_unused xpsgtr_suspend(struct device *dev) { struct xpsgtr_dev *gtr_dev = dev_get_drvdata(dev); @@ -827,7 +826,7 @@ static int xpsgtr_suspend(struct device *dev) return 0; } -static int xpsgtr_resume(struct device *dev) +static int __maybe_unused xpsgtr_resume(struct device *dev) { struct xpsgtr_dev *gtr_dev = dev_get_drvdata(dev); unsigned int icm_cfg0, icm_cfg1; @@ -854,7 +853,6 @@ static int xpsgtr_resume(struct device *dev) return 0; } -#endif /* CONFIG_PM */ static const struct dev_pm_ops xpsgtr_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(xpsgtr_suspend, xpsgtr_resume) -- cgit v1.2.3-59-g8ed1b From 36847f9e3e56c192ef95e7669df38189443530a0 Mon Sep 17 00:00:00 2001 From: Richard Gong Date: Mon, 15 Jun 2020 09:29:05 -0500 Subject: firmware: stratix10-svc: correct reconfig flag and timeout values Correct the incorrect flag value for COMMAND_RECONFIG_FLAG_PARTIAL and increase FPGA reconfig timeout values so that Intel service layer and FPGA manager drivers can work with all versions of firmware. Signed-off-by: Richard Gong Link: https://lore.kernel.org/r/1592231348-31334-2-git-send-email-richard.gong@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- include/linux/firmware/intel/stratix10-svc-client.h | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/include/linux/firmware/intel/stratix10-svc-client.h b/include/linux/firmware/intel/stratix10-svc-client.h index 64213c3e82f5..040bc3f3bedd 100644 --- a/include/linux/firmware/intel/stratix10-svc-client.h +++ b/include/linux/firmware/intel/stratix10-svc-client.h @@ -54,18 +54,17 @@ * Flag bit for COMMAND_RECONFIG * * COMMAND_RECONFIG_FLAG_PARTIAL: - * Set to FPGA configuration type (full or partial), the default - * is full reconfig. + * Set to FPGA configuration type (full or partial). */ -#define COMMAND_RECONFIG_FLAG_PARTIAL 0 +#define COMMAND_RECONFIG_FLAG_PARTIAL 1 /** * Timeout settings for service clients: * timeout value used in Stratix10 FPGA manager driver. * timeout value used in RSU driver */ -#define SVC_RECONFIG_REQUEST_TIMEOUT_MS 100 -#define SVC_RECONFIG_BUFFER_TIMEOUT_MS 240 +#define SVC_RECONFIG_REQUEST_TIMEOUT_MS 300 +#define SVC_RECONFIG_BUFFER_TIMEOUT_MS 720 #define SVC_RSU_REQUEST_TIMEOUT_MS 300 struct stratix10_svc_chan; -- cgit v1.2.3-59-g8ed1b From bf0e5bf68a207b14727caf13da576339590a9504 Mon Sep 17 00:00:00 2001 From: Richard Gong Date: Mon, 15 Jun 2020 09:29:06 -0500 Subject: firmware: stratix10-svc: extend svc to support new RSU features Extend Intel Stratix10 service layer driver to support new RSU DCMF versions and max retry parameter. DCMF = Decision Configuration Management Firmware. The max retry parameter is the maximum times the images is allowed to reload itself before giving up and starting RSU failover flow. Signed-off-by: Richard Gong Link: https://lore.kernel.org/r/1592231348-31334-3-git-send-email-richard.gong@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/firmware/stratix10-svc.c | 17 +++++++++ include/linux/firmware/intel/stratix10-smc.h | 43 ++++++++++++++++++++++ .../linux/firmware/intel/stratix10-svc-client.h | 8 ++++ 3 files changed, 68 insertions(+) diff --git a/drivers/firmware/stratix10-svc.c b/drivers/firmware/stratix10-svc.c index e0db8dbfc9d1..3aa489dba30a 100644 --- a/drivers/firmware/stratix10-svc.c +++ b/drivers/firmware/stratix10-svc.c @@ -305,9 +305,15 @@ static void svc_thread_recv_status_ok(struct stratix10_svc_data *p_data, cb_data->status = BIT(SVC_STATUS_COMPLETED); break; case COMMAND_RSU_RETRY: + case COMMAND_RSU_MAX_RETRY: cb_data->status = BIT(SVC_STATUS_OK); cb_data->kaddr1 = &res.a1; break; + case COMMAND_RSU_DCMF_VERSION: + cb_data->status = BIT(SVC_STATUS_OK); + cb_data->kaddr1 = &res.a1; + cb_data->kaddr2 = &res.a2; + break; default: pr_warn("it shouldn't happen\n"); break; @@ -406,6 +412,16 @@ static int svc_normal_to_secure_thread(void *data) a1 = 0; a2 = 0; break; + case COMMAND_RSU_MAX_RETRY: + a0 = INTEL_SIP_SMC_RSU_MAX_RETRY; + a1 = 0; + a2 = 0; + break; + case COMMAND_RSU_DCMF_VERSION: + a0 = INTEL_SIP_SMC_RSU_DCMF_VERSION; + a1 = 0; + a2 = 0; + break; default: pr_warn("it shouldn't happen\n"); break; @@ -474,6 +490,7 @@ static int svc_normal_to_secure_thread(void *data) * doesn't support RSU notify or retry */ if ((pdata->command == COMMAND_RSU_RETRY) || + (pdata->command == COMMAND_RSU_MAX_RETRY) || (pdata->command == COMMAND_RSU_NOTIFY)) { cbdata->status = BIT(SVC_STATUS_NO_SUPPORT); diff --git a/include/linux/firmware/intel/stratix10-smc.h b/include/linux/firmware/intel/stratix10-smc.h index 682dbf694007..c3e5ab014caf 100644 --- a/include/linux/firmware/intel/stratix10-smc.h +++ b/include/linux/firmware/intel/stratix10-smc.h @@ -361,3 +361,46 @@ INTEL_SIP_SMC_FAST_CALL_VAL(INTEL_SIP_SMC_FUNCID_FPGA_CONFIG_COMPLETED_WRITE) #define INTEL_SIP_SMC_FUNCID_RSU_RETRY_COUNTER 15 #define INTEL_SIP_SMC_RSU_RETRY_COUNTER \ INTEL_SIP_SMC_FAST_CALL_VAL(INTEL_SIP_SMC_FUNCID_RSU_RETRY_COUNTER) + +/** + * Request INTEL_SIP_SMC_RSU_DCMF_VERSION + * + * Sync call used by service driver at EL1 to query DCMF (Decision + * Configuration Management Firmware) version from FW + * + * Call register usage: + * a0 INTEL_SIP_SMC_RSU_DCMF_VERSION + * a1-7 not used + * + * Return status + * a0 INTEL_SIP_SMC_STATUS_OK + * a1 dcmf1 | dcmf0 + * a2 dcmf3 | dcmf2 + * + * Or + * + * a0 INTEL_SIP_SMC_RSU_ERROR + */ +#define INTEL_SIP_SMC_FUNCID_RSU_DCMF_VERSION 16 +#define INTEL_SIP_SMC_RSU_DCMF_VERSION \ + INTEL_SIP_SMC_FAST_CALL_VAL(INTEL_SIP_SMC_FUNCID_RSU_DCMF_VERSION) + +/** + * Request INTEL_SIP_SMC_RSU_MAX_RETRY + * + * Sync call used by service driver at EL1 to query max retry value from FW + * + * Call register usage: + * a0 INTEL_SIP_SMC_RSU_MAX_RETRY + * a1-7 not used + * + * Return status + * a0 INTEL_SIP_SMC_STATUS_OK + * a1 max retry value + * + * Or + * a0 INTEL_SIP_SMC_RSU_ERROR + */ +#define INTEL_SIP_SMC_FUNCID_RSU_MAX_RETRY 18 +#define INTEL_SIP_SMC_RSU_MAX_RETRY \ + INTEL_SIP_SMC_FAST_CALL_VAL(INTEL_SIP_SMC_FUNCID_RSU_MAX_RETRY) diff --git a/include/linux/firmware/intel/stratix10-svc-client.h b/include/linux/firmware/intel/stratix10-svc-client.h index 040bc3f3bedd..a93d85932eb9 100644 --- a/include/linux/firmware/intel/stratix10-svc-client.h +++ b/include/linux/firmware/intel/stratix10-svc-client.h @@ -98,6 +98,12 @@ struct stratix10_svc_chan; * * @COMMAND_RSU_RETRY: query firmware for the current image's retry counter, * return status is SVC_STATUS_OK or SVC_STATUS_ERROR + * + * @COMMAND_RSU_MAX_RETRY: query firmware for the max retry value, + * return status is SVC_STATUS_OK or SVC_STATUS_ERROR + * + * @COMMAND_RSU_DCMF_VERSION: query firmware for the DCMF version, return status + * is SVC_STATUS_OK or SVC_STATUS_ERROR */ enum stratix10_svc_command_code { COMMAND_NOOP = 0, @@ -109,6 +115,8 @@ enum stratix10_svc_command_code { COMMAND_RSU_UPDATE, COMMAND_RSU_NOTIFY, COMMAND_RSU_RETRY, + COMMAND_RSU_MAX_RETRY, + COMMAND_RSU_DCMF_VERSION, }; /** -- cgit v1.2.3-59-g8ed1b From 75bc73fc0188ef3120365704a3d8ffd865666ae1 Mon Sep 17 00:00:00 2001 From: Richard Gong Date: Mon, 15 Jun 2020 09:29:07 -0500 Subject: firmware: stratix10-rsu: extend rsu driver for new features Extend Intel Remote System Update (RSU) driver to get decision configuration management firmware (DCMF) versions and max retry parameter value. Signed-off-by: Richard Gong Link: https://lore.kernel.org/r/1592231348-31334-4-git-send-email-richard.gong@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/firmware/stratix10-rsu.c | 166 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 161 insertions(+), 5 deletions(-) diff --git a/drivers/firmware/stratix10-rsu.c b/drivers/firmware/stratix10-rsu.c index 4379475c99ed..0686338baafc 100644 --- a/drivers/firmware/stratix10-rsu.c +++ b/drivers/firmware/stratix10-rsu.c @@ -20,10 +20,16 @@ #define RSU_VERSION_MASK GENMASK_ULL(63, 32) #define RSU_ERROR_LOCATION_MASK GENMASK_ULL(31, 0) #define RSU_ERROR_DETAIL_MASK GENMASK_ULL(63, 32) +#define RSU_DCMF0_MASK GENMASK_ULL(31, 0) +#define RSU_DCMF1_MASK GENMASK_ULL(63, 32) +#define RSU_DCMF2_MASK GENMASK_ULL(31, 0) +#define RSU_DCMF3_MASK GENMASK_ULL(63, 32) #define RSU_TIMEOUT (msecs_to_jiffies(SVC_RSU_REQUEST_TIMEOUT_MS)) -#define INVALID_RETRY_COUNTER 0xFFFFFFFF +#define INVALID_RETRY_COUNTER 0xFF +#define INVALID_DCMF_VERSION 0xFF + typedef void (*rsu_callback)(struct stratix10_svc_client *client, struct stratix10_svc_cb_data *data); @@ -35,11 +41,16 @@ typedef void (*rsu_callback)(struct stratix10_svc_client *client, * @lock: a mutex to protect callback completion state * @status.current_image: address of image currently running in flash * @status.fail_image: address of failed image in flash - * @status.version: the version number of RSU firmware + * @status.version: the interface version number of RSU firmware * @status.state: the state of RSU system * @status.error_details: error code * @status.error_location: the error offset inside the image that failed + * @dcmf_version.dcmf0: Quartus dcmf0 version + * @dcmf_version.dcmf1: Quartus dcmf1 version + * @dcmf_version.dcmf2: Quartus dcmf2 version + * @dcmf_version.dcmf3: Quartus dcmf3 version * @retry_counter: the current image's retry counter + * @max_retry: the preset max retry value */ struct stratix10_rsu_priv { struct stratix10_svc_chan *chan; @@ -54,7 +65,16 @@ struct stratix10_rsu_priv { unsigned int error_details; unsigned int error_location; } status; + + struct { + unsigned int dcmf0; + unsigned int dcmf1; + unsigned int dcmf2; + unsigned int dcmf3; + } dcmf_version; + unsigned int retry_counter; + unsigned int max_retry; }; /** @@ -109,7 +129,7 @@ static void rsu_command_callback(struct stratix10_svc_client *client, struct stratix10_rsu_priv *priv = client->priv; if (data->status == BIT(SVC_STATUS_NO_SUPPORT)) - dev_warn(client->dev, "Secure FW doesn't support notify\n"); + dev_warn(client->dev, "FW doesn't support notify\n"); else if (data->status == BIT(SVC_STATUS_ERROR)) dev_err(client->dev, "Failure, returned status is %lu\n", BIT(data->status)); @@ -119,7 +139,7 @@ static void rsu_command_callback(struct stratix10_svc_client *client, /** * rsu_retry_callback() - Callback from Intel service layer for getting - * the current image's retry counter from firmware + * the current image's retry counter from the firmware * @client: pointer to client * @data: pointer to callback data structure * @@ -136,7 +156,7 @@ static void rsu_retry_callback(struct stratix10_svc_client *client, if (data->status == BIT(SVC_STATUS_OK)) priv->retry_counter = *counter; else if (data->status == BIT(SVC_STATUS_NO_SUPPORT)) - dev_warn(client->dev, "Secure FW doesn't support retry\n"); + dev_warn(client->dev, "FW doesn't support retry\n"); else dev_err(client->dev, "Failed to get retry counter %lu\n", BIT(data->status)); @@ -144,6 +164,57 @@ static void rsu_retry_callback(struct stratix10_svc_client *client, complete(&priv->completion); } +/** + * rsu_max_retry_callback() - Callback from Intel service layer for getting + * the max retry value from the firmware + * @client: pointer to client + * @data: pointer to callback data structure + * + * Callback from Intel service layer for max retry. + */ +static void rsu_max_retry_callback(struct stratix10_svc_client *client, + struct stratix10_svc_cb_data *data) +{ + struct stratix10_rsu_priv *priv = client->priv; + unsigned int *max_retry = (unsigned int *)data->kaddr1; + + if (data->status == BIT(SVC_STATUS_OK)) + priv->max_retry = *max_retry; + else if (data->status == BIT(SVC_STATUS_NO_SUPPORT)) + dev_warn(client->dev, "FW doesn't support max retry\n"); + else + dev_err(client->dev, "Failed to get max retry %lu\n", + BIT(data->status)); + + complete(&priv->completion); +} + +/** + * rsu_dcmf_version_callback() - Callback from Intel service layer for getting + * the DCMF version + * @client: pointer to client + * @data: pointer to callback data structure + * + * Callback from Intel service layer for DCMF version number + */ +static void rsu_dcmf_version_callback(struct stratix10_svc_client *client, + struct stratix10_svc_cb_data *data) +{ + struct stratix10_rsu_priv *priv = client->priv; + unsigned long long *value1 = (unsigned long long *)data->kaddr1; + unsigned long long *value2 = (unsigned long long *)data->kaddr2; + + if (data->status == BIT(SVC_STATUS_OK)) { + priv->dcmf_version.dcmf0 = FIELD_GET(RSU_DCMF0_MASK, *value1); + priv->dcmf_version.dcmf1 = FIELD_GET(RSU_DCMF1_MASK, *value1); + priv->dcmf_version.dcmf2 = FIELD_GET(RSU_DCMF2_MASK, *value2); + priv->dcmf_version.dcmf3 = FIELD_GET(RSU_DCMF3_MASK, *value2); + } else + dev_err(client->dev, "failed to get DCMF version\n"); + + complete(&priv->completion); +} + /** * rsu_send_msg() - send a message to Intel service layer * @priv: pointer to rsu private data @@ -282,6 +353,61 @@ static ssize_t retry_counter_show(struct device *dev, return sprintf(buf, "0x%08x\n", priv->retry_counter); } +static ssize_t max_retry_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct stratix10_rsu_priv *priv = dev_get_drvdata(dev); + + if (!priv) + return -ENODEV; + + return sprintf(buf, "0x%08x\n", priv->max_retry); +} + +static ssize_t dcmf0_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct stratix10_rsu_priv *priv = dev_get_drvdata(dev); + + if (!priv) + return -ENODEV; + + return sprintf(buf, "0x%08x\n", priv->dcmf_version.dcmf0); +} + +static ssize_t dcmf1_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct stratix10_rsu_priv *priv = dev_get_drvdata(dev); + + if (!priv) + return -ENODEV; + + return sprintf(buf, "0x%08x\n", priv->dcmf_version.dcmf1); +} + +static ssize_t dcmf2_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct stratix10_rsu_priv *priv = dev_get_drvdata(dev); + + if (!priv) + return -ENODEV; + + return sprintf(buf, "0x%08x\n", priv->dcmf_version.dcmf2); +} + +static ssize_t dcmf3_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct stratix10_rsu_priv *priv = dev_get_drvdata(dev); + + if (!priv) + return -ENODEV; + + return sprintf(buf, "0x%08x\n", priv->dcmf_version.dcmf3); +} + static ssize_t reboot_image_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) @@ -353,6 +479,11 @@ static DEVICE_ATTR_RO(version); static DEVICE_ATTR_RO(error_location); static DEVICE_ATTR_RO(error_details); static DEVICE_ATTR_RO(retry_counter); +static DEVICE_ATTR_RO(max_retry); +static DEVICE_ATTR_RO(dcmf0); +static DEVICE_ATTR_RO(dcmf1); +static DEVICE_ATTR_RO(dcmf2); +static DEVICE_ATTR_RO(dcmf3); static DEVICE_ATTR_WO(reboot_image); static DEVICE_ATTR_WO(notify); @@ -364,6 +495,11 @@ static struct attribute *rsu_attrs[] = { &dev_attr_error_location.attr, &dev_attr_error_details.attr, &dev_attr_retry_counter.attr, + &dev_attr_max_retry.attr, + &dev_attr_dcmf0.attr, + &dev_attr_dcmf1.attr, + &dev_attr_dcmf2.attr, + &dev_attr_dcmf3.attr, &dev_attr_reboot_image.attr, &dev_attr_notify.attr, NULL @@ -391,6 +527,11 @@ static int stratix10_rsu_probe(struct platform_device *pdev) priv->status.version = 0; priv->status.state = 0; priv->retry_counter = INVALID_RETRY_COUNTER; + priv->dcmf_version.dcmf0 = INVALID_DCMF_VERSION; + priv->dcmf_version.dcmf1 = INVALID_DCMF_VERSION; + priv->dcmf_version.dcmf2 = INVALID_DCMF_VERSION; + priv->dcmf_version.dcmf3 = INVALID_DCMF_VERSION; + priv->max_retry = INVALID_RETRY_COUNTER; mutex_init(&priv->lock); priv->chan = stratix10_svc_request_channel_byname(&priv->client, @@ -412,12 +553,27 @@ static int stratix10_rsu_probe(struct platform_device *pdev) stratix10_svc_free_channel(priv->chan); } + /* get DCMF version from firmware */ + ret = rsu_send_msg(priv, COMMAND_RSU_DCMF_VERSION, + 0, rsu_dcmf_version_callback); + if (ret) { + dev_err(dev, "Error, getting DCMF version %i\n", ret); + stratix10_svc_free_channel(priv->chan); + } + ret = rsu_send_msg(priv, COMMAND_RSU_RETRY, 0, rsu_retry_callback); if (ret) { dev_err(dev, "Error, getting RSU retry %i\n", ret); stratix10_svc_free_channel(priv->chan); } + ret = rsu_send_msg(priv, COMMAND_RSU_MAX_RETRY, 0, + rsu_max_retry_callback); + if (ret) { + dev_err(dev, "Error, getting RSU max retry %i\n", ret); + stratix10_svc_free_channel(priv->chan); + } + return ret; } -- cgit v1.2.3-59-g8ed1b From f38018968fd01edbc18adfcf6512ccc397af49ef Mon Sep 17 00:00:00 2001 From: Richard Gong Date: Mon, 15 Jun 2020 09:29:08 -0500 Subject: firmware: rsu: add device attributes to sysfs interface Add the following device attributes to Intel Stratix10 Remote System Update (RSU) sysfs interface: dcmf0 dcmf1 dcmf2 dcmf3 max_retry Signed-off-by: Richard Gong Link: https://lore.kernel.org/r/1592231348-31334-5-git-send-email-richard.gong@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- .../testing/sysfs-devices-platform-stratix10-rsu | 36 ++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-devices-platform-stratix10-rsu b/Documentation/ABI/testing/sysfs-devices-platform-stratix10-rsu index ae9af984471a..a8daceb4a956 100644 --- a/Documentation/ABI/testing/sysfs-devices-platform-stratix10-rsu +++ b/Documentation/ABI/testing/sysfs-devices-platform-stratix10-rsu @@ -126,3 +126,39 @@ Description: 1 no action 0 firmware record the notify code defined in b[15:0]. + +What: /sys/devices/platform/stratix10-rsu.0/dcmf0 +Date: June 2020 +KernelVersion: 5.8 +Contact: Richard Gong +Description: + (RO) Decision firmware copy 0 version information. + +What: /sys/devices/platform/stratix10-rsu.0/dcmf1 +Date: June 2020 +KernelVersion: 5.8 +Contact: Richard Gong +Description: + (RO) Decision firmware copy 1 version information. + +What: /sys/devices/platform/stratix10-rsu.0/dcmf2 +Date: June 2020 +KernelVersion: 5.8 +Contact: Richard Gong +Description: + (RO) Decision firmware copy 2 version information. + +What: /sys/devices/platform/stratix10-rsu.0/dcmf3 +Date: June 2020 +KernelVersion: 5.8 +Contact: Richard Gong +Description: + (RO) Decision firmware copy 3 version information. + +What: /sys/devices/platform/stratix10-rsu.0/max_retry +Date: June 2020 +KernelVersion: 5.8 +Contact: Richard Gong +Description: + (RO) max retry parameter is stored in the firmware + decision IO section, as a byte located at offset 0x18c. -- cgit v1.2.3-59-g8ed1b From 52f944ee5d39b6b93797480f9181d3d40ed0d07b Mon Sep 17 00:00:00 2001 From: Richard Gong Date: Wed, 3 Jun 2020 09:06:52 -0500 Subject: firmware: stratix10-rsu: fix warnings Patch fixes sparse warnings: using plain integer as NULL pointer. Replaces equal to with logical not operator. Reported-by: kbuild test robot Signed-off-by: Richard Gong Link: https://lore.kernel.org/r/1591193212-15082-1-git-send-email-richard.gong@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/firmware/stratix10-rsu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/firmware/stratix10-rsu.c b/drivers/firmware/stratix10-rsu.c index 0686338baafc..9378075d04e9 100644 --- a/drivers/firmware/stratix10-rsu.c +++ b/drivers/firmware/stratix10-rsu.c @@ -416,7 +416,7 @@ static ssize_t reboot_image_store(struct device *dev, unsigned long address; int ret; - if (priv == 0) + if (!priv) return -ENODEV; ret = kstrtoul(buf, 0, &address); @@ -441,7 +441,7 @@ static ssize_t notify_store(struct device *dev, unsigned long status; int ret; - if (priv == 0) + if (!priv) return -ENODEV; ret = kstrtoul(buf, 0, &status); -- cgit v1.2.3-59-g8ed1b From 7aca462b133300c16213e214bb96e516f1f2784e Mon Sep 17 00:00:00 2001 From: Esben Haabendal Date: Wed, 1 Jul 2020 16:56:59 +0200 Subject: uio_pdrv_genirq: Allow use with non-page-aligned memory resources Similar to the changes made in 270579d95f82 ("uio_mf624: Align memory regions to page size and set correct offsets"), this will allow uio_pdrv_genirq devices to expose memory regions that is not page-aligned, requiring the users to respect the offset sysfs attribute (as implemented in libuio). Signed-off-by: Esben Haabendal Link: https://lore.kernel.org/r/20200701145659.3978-4-esben@geanix.com Signed-off-by: Greg Kroah-Hartman --- drivers/uio/uio_pdrv_genirq.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/uio/uio_pdrv_genirq.c b/drivers/uio/uio_pdrv_genirq.c index ae319ef3a832..11e11df0daf8 100644 --- a/drivers/uio/uio_pdrv_genirq.c +++ b/drivers/uio/uio_pdrv_genirq.c @@ -187,8 +187,10 @@ static int uio_pdrv_genirq_probe(struct platform_device *pdev) } uiomem->memtype = UIO_MEM_PHYS; - uiomem->addr = r->start; - uiomem->size = resource_size(r); + uiomem->addr = r->start & PAGE_MASK; + uiomem->offs = r->start & ~PAGE_MASK; + uiomem->size = (uiomem->offs + resource_size(r) + + PAGE_SIZE - 1) & PAGE_MASK; uiomem->name = r->name; ++uiomem; } -- cgit v1.2.3-59-g8ed1b From 415abcdfd4b8326d679cd0c78bee00b5ceac9718 Mon Sep 17 00:00:00 2001 From: Thommy Jakobsson Date: Sun, 28 Jun 2020 16:12:29 +0200 Subject: uio: disable lazy irq disable to avoid double fire uio_pdrv_genirq and uio_dmem_genirq interrupts are handled in userspace. So the condition for the interrupt hasn't normally not been cleared when top half returns. disable_irq_nosync is called in top half, but since that normally is lazy the irq isn't actually disabled. For level triggered interrupts this will always result in a spurious additional fire since the level in to the interrupt controller still is active. The actual interrupt handler isn't run though since this spurious irq is just recorded, and later on discared (for level). This commit disables lazy masking for level triggered interrupts. It leaves edge triggered interrupts as before, because they work with the lazy scheme. All other UIO drivers already seem to clear the interrupt cause at driver levels. Example of double fire. First goes all the way up to uio_pdrv_genirq_handler, second is terminated in handle_fasteoi_irq and marked as pending. -0 [000] d... 8.245870: gic_handle_irq: irq 29 -0 [000] d.h. 8.245873: uio_pdrv_genirq_handler: disable irq 29 -0 [000] d... 8.245878: gic_handle_irq: irq 29 -0 [000] d.h. 8.245880: handle_fasteoi_irq: irq 29 PENDING HInt-34 [001] d... 8.245897: uio_pdrv_genirq_irqcontrol: enable irq 29 Tested on 5.7rc2 using uio_pdrv_genirq and a custom Xilinx MPSoC board. Signed-off-by: Thommy Jakobsson Link: https://lore.kernel.org/r/20200628141229.16121-1-thommyj@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/uio/uio_dmem_genirq.c | 19 +++++++++++++++++++ drivers/uio/uio_pdrv_genirq.c | 18 ++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/drivers/uio/uio_dmem_genirq.c b/drivers/uio/uio_dmem_genirq.c index 6e27fe4fcca3..ec7f66f4555a 100644 --- a/drivers/uio/uio_dmem_genirq.c +++ b/drivers/uio/uio_dmem_genirq.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -199,6 +200,24 @@ static int uio_dmem_genirq_probe(struct platform_device *pdev) goto bad1; uioinfo->irq = ret; } + + if (uioinfo->irq) { + struct irq_data *irq_data = irq_get_irq_data(uioinfo->irq); + + /* + * If a level interrupt, dont do lazy disable. Otherwise the + * irq will fire again since clearing of the actual cause, on + * device level, is done in userspace + * irqd_is_level_type() isn't used since isn't valid until + * irq is configured. + */ + if (irq_data && + irqd_get_trigger_type(irq_data) & IRQ_TYPE_LEVEL_MASK) { + dev_dbg(&pdev->dev, "disable lazy unmask\n"); + irq_set_status_flags(uioinfo->irq, IRQ_DISABLE_UNLAZY); + } + } + uiomem = &uioinfo->mem[0]; for (i = 0; i < pdev->num_resources; ++i) { diff --git a/drivers/uio/uio_pdrv_genirq.c b/drivers/uio/uio_pdrv_genirq.c index 11e11df0daf8..5f396c37a34f 100644 --- a/drivers/uio/uio_pdrv_genirq.c +++ b/drivers/uio/uio_pdrv_genirq.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -171,6 +172,23 @@ static int uio_pdrv_genirq_probe(struct platform_device *pdev) } } + if (uioinfo->irq) { + struct irq_data *irq_data = irq_get_irq_data(uioinfo->irq); + + /* + * If a level interrupt, dont do lazy disable. Otherwise the + * irq will fire again since clearing of the actual cause, on + * device level, is done in userspace + * irqd_is_level_type() isn't used since isn't valid until + * irq is configured. + */ + if (irq_data && + irqd_get_trigger_type(irq_data) & IRQ_TYPE_LEVEL_MASK) { + dev_dbg(&pdev->dev, "disable lazy unmask\n"); + irq_set_status_flags(uioinfo->irq, IRQ_DISABLE_UNLAZY); + } + } + uiomem = &uioinfo->mem[0]; for (i = 0; i < pdev->num_resources; ++i) { -- cgit v1.2.3-59-g8ed1b From fe6a3d6521227e49857d0d6caabbdae3bd4aef89 Mon Sep 17 00:00:00 2001 From: Xu Yilun Date: Tue, 16 Jun 2020 12:08:45 +0800 Subject: fpga: dfl: afu: add interrupt support for port error reporting Error reporting interrupt is very useful to notify users that some errors are detected by the hardware. Once users are notified, they could query hardware logged error states, no need to continuously poll on these states. This patch adds interrupt support for port error reporting sub feature. It follows the common DFL interrupt notification and handling mechanism, implements two ioctl commands below for user to query number of irqs supported, and set/unset interrupt triggers. Ioctls: * DFL_FPGA_PORT_ERR_GET_IRQ_NUM get the number of irqs, which is used to determine whether/how many interrupts error reporting feature supports. * DFL_FPGA_PORT_ERR_SET_IRQ set/unset given eventfds as error interrupt triggers. Signed-off-by: Luwei Kang Signed-off-by: Wu Hao Signed-off-by: Xu Yilun Reviewed-by: Marcelo Tosatti Acked-by: Wu Hao Signed-off-by: Moritz Fischer --- drivers/fpga/dfl-afu-error.c | 17 +++++++++++++++++ drivers/fpga/dfl-afu-main.c | 4 ++++ include/uapi/linux/fpga-dfl.h | 23 +++++++++++++++++++++++ 3 files changed, 44 insertions(+) diff --git a/drivers/fpga/dfl-afu-error.c b/drivers/fpga/dfl-afu-error.c index c1467ae1a6b6..c4691187cca9 100644 --- a/drivers/fpga/dfl-afu-error.c +++ b/drivers/fpga/dfl-afu-error.c @@ -14,6 +14,7 @@ * Mitchel Henry */ +#include #include #include "dfl-afu.h" @@ -219,6 +220,21 @@ static void port_err_uinit(struct platform_device *pdev, afu_port_err_mask(&pdev->dev, true); } +static long +port_err_ioctl(struct platform_device *pdev, struct dfl_feature *feature, + unsigned int cmd, unsigned long arg) +{ + switch (cmd) { + case DFL_FPGA_PORT_ERR_GET_IRQ_NUM: + return dfl_feature_ioctl_get_num_irqs(pdev, feature, arg); + case DFL_FPGA_PORT_ERR_SET_IRQ: + return dfl_feature_ioctl_set_irq(pdev, feature, arg); + default: + dev_dbg(&pdev->dev, "%x cmd not handled", cmd); + return -ENODEV; + } +} + const struct dfl_feature_id port_err_id_table[] = { {.id = PORT_FEATURE_ID_ERROR,}, {0,} @@ -227,4 +243,5 @@ const struct dfl_feature_id port_err_id_table[] = { const struct dfl_feature_ops port_err_ops = { .init = port_err_init, .uinit = port_err_uinit, + .ioctl = port_err_ioctl, }; diff --git a/drivers/fpga/dfl-afu-main.c b/drivers/fpga/dfl-afu-main.c index b0c31789a909..357cd5d9b267 100644 --- a/drivers/fpga/dfl-afu-main.c +++ b/drivers/fpga/dfl-afu-main.c @@ -577,6 +577,7 @@ static int afu_release(struct inode *inode, struct file *filp) { struct platform_device *pdev = filp->private_data; struct dfl_feature_platform_data *pdata; + struct dfl_feature *feature; dev_dbg(&pdev->dev, "Device File Release\n"); @@ -586,6 +587,9 @@ static int afu_release(struct inode *inode, struct file *filp) dfl_feature_dev_use_end(pdata); if (!dfl_feature_dev_use_count(pdata)) { + dfl_fpga_dev_for_each_feature(pdata, feature) + dfl_fpga_set_irq_triggers(feature, 0, + feature->nr_irqs, NULL); __port_reset(pdev); afu_dma_region_destroy(pdata); } diff --git a/include/uapi/linux/fpga-dfl.h b/include/uapi/linux/fpga-dfl.h index 7331350f3067..6c71c9d7e926 100644 --- a/include/uapi/linux/fpga-dfl.h +++ b/include/uapi/linux/fpga-dfl.h @@ -164,6 +164,29 @@ struct dfl_fpga_irq_set { __s32 evtfds[]; }; +/** + * DFL_FPGA_PORT_ERR_GET_IRQ_NUM - _IOR(DFL_FPGA_MAGIC, DFL_PORT_BASE + 5, + * __u32 num_irqs) + * + * Get the number of irqs supported by the fpga port error reporting private + * feature. Currently hardware supports up to 1 irq. + * Return: 0 on success, -errno on failure. + */ +#define DFL_FPGA_PORT_ERR_GET_IRQ_NUM _IOR(DFL_FPGA_MAGIC, \ + DFL_PORT_BASE + 5, __u32) + +/** + * DFL_FPGA_PORT_ERR_SET_IRQ - _IOW(DFL_FPGA_MAGIC, DFL_PORT_BASE + 6, + * struct dfl_fpga_irq_set) + * + * Set fpga port error reporting interrupt trigger if evtfds[n] is valid. + * Unset related interrupt trigger if evtfds[n] is a negative value. + * Return: 0 on success, -errno on failure. + */ +#define DFL_FPGA_PORT_ERR_SET_IRQ _IOW(DFL_FPGA_MAGIC, \ + DFL_PORT_BASE + 6, \ + struct dfl_fpga_irq_set) + /* IOCTLs for FME file descriptor */ /** -- cgit v1.2.3-59-g8ed1b From d43f20bae5173ba431526040c320c36fdd4f086d Mon Sep 17 00:00:00 2001 From: Xu Yilun Date: Tue, 16 Jun 2020 12:08:46 +0800 Subject: fpga: dfl: fme: add interrupt support for global error reporting Error reporting interrupt is very useful to notify users that some errors are detected by the hardware. Once users are notified, they could query hardware logged error states, no need to continuously poll on these states. This patch adds interrupt support for fme global error reporting sub feature. It follows the common DFL interrupt notification and handling mechanism. And it implements two ioctls below for user to query number of irqs supported, and set/unset interrupt triggers. Ioctls: * DFL_FPGA_FME_ERR_GET_IRQ_NUM get the number of irqs, which is used to determine whether/how many interrupts fme error reporting feature supports. * DFL_FPGA_FME_ERR_SET_IRQ set/unset given eventfds as fme error reporting interrupt triggers. Signed-off-by: Luwei Kang Signed-off-by: Wu Hao Signed-off-by: Xu Yilun Reviewed-by: Marcelo Tosatti Acked-by: Wu Hao Signed-off-by: Moritz Fischer --- drivers/fpga/dfl-fme-error.c | 18 ++++++++++++++++++ drivers/fpga/dfl-fme-main.c | 6 ++++++ include/uapi/linux/fpga-dfl.h | 23 +++++++++++++++++++++++ 3 files changed, 47 insertions(+) diff --git a/drivers/fpga/dfl-fme-error.c b/drivers/fpga/dfl-fme-error.c index f897d414b923..51c2892ec06d 100644 --- a/drivers/fpga/dfl-fme-error.c +++ b/drivers/fpga/dfl-fme-error.c @@ -15,6 +15,7 @@ * Mitchel, Henry */ +#include #include #include "dfl.h" @@ -348,6 +349,22 @@ static void fme_global_err_uinit(struct platform_device *pdev, fme_err_mask(&pdev->dev, true); } +static long +fme_global_error_ioctl(struct platform_device *pdev, + struct dfl_feature *feature, + unsigned int cmd, unsigned long arg) +{ + switch (cmd) { + case DFL_FPGA_FME_ERR_GET_IRQ_NUM: + return dfl_feature_ioctl_get_num_irqs(pdev, feature, arg); + case DFL_FPGA_FME_ERR_SET_IRQ: + return dfl_feature_ioctl_set_irq(pdev, feature, arg); + default: + dev_dbg(&pdev->dev, "%x cmd not handled", cmd); + return -ENODEV; + } +} + const struct dfl_feature_id fme_global_err_id_table[] = { {.id = FME_FEATURE_ID_GLOBAL_ERR,}, {0,} @@ -356,4 +373,5 @@ const struct dfl_feature_id fme_global_err_id_table[] = { const struct dfl_feature_ops fme_global_err_ops = { .init = fme_global_err_init, .uinit = fme_global_err_uinit, + .ioctl = fme_global_error_ioctl, }; diff --git a/drivers/fpga/dfl-fme-main.c b/drivers/fpga/dfl-fme-main.c index fc210d4e1863..77ea04d4edbe 100644 --- a/drivers/fpga/dfl-fme-main.c +++ b/drivers/fpga/dfl-fme-main.c @@ -620,11 +620,17 @@ static int fme_release(struct inode *inode, struct file *filp) { struct dfl_feature_platform_data *pdata = filp->private_data; struct platform_device *pdev = pdata->dev; + struct dfl_feature *feature; dev_dbg(&pdev->dev, "Device File Release\n"); mutex_lock(&pdata->lock); dfl_feature_dev_use_end(pdata); + + if (!dfl_feature_dev_use_count(pdata)) + dfl_fpga_dev_for_each_feature(pdata, feature) + dfl_fpga_set_irq_triggers(feature, 0, + feature->nr_irqs, NULL); mutex_unlock(&pdata->lock); return 0; diff --git a/include/uapi/linux/fpga-dfl.h b/include/uapi/linux/fpga-dfl.h index 6c71c9d7e926..b6495ea412ec 100644 --- a/include/uapi/linux/fpga-dfl.h +++ b/include/uapi/linux/fpga-dfl.h @@ -230,4 +230,27 @@ struct dfl_fpga_fme_port_pr { */ #define DFL_FPGA_FME_PORT_ASSIGN _IOW(DFL_FPGA_MAGIC, DFL_FME_BASE + 2, int) +/** + * DFL_FPGA_FME_ERR_GET_IRQ_NUM - _IOR(DFL_FPGA_MAGIC, DFL_FME_BASE + 3, + * __u32 num_irqs) + * + * Get the number of irqs supported by the fpga fme error reporting private + * feature. Currently hardware supports up to 1 irq. + * Return: 0 on success, -errno on failure. + */ +#define DFL_FPGA_FME_ERR_GET_IRQ_NUM _IOR(DFL_FPGA_MAGIC, \ + DFL_FME_BASE + 3, __u32) + +/** + * DFL_FPGA_FME_ERR_SET_IRQ - _IOW(DFL_FPGA_MAGIC, DFL_FME_BASE + 4, + * struct dfl_fpga_irq_set) + * + * Set fpga fme error reporting interrupt trigger if evtfds[n] is valid. + * Unset related interrupt trigger if evtfds[n] is a negative value. + * Return: 0 on success, -errno on failure. + */ +#define DFL_FPGA_FME_ERR_SET_IRQ _IOW(DFL_FPGA_MAGIC, \ + DFL_FME_BASE + 4, \ + struct dfl_fpga_irq_set) + #endif /* _UAPI_LINUX_FPGA_DFL_H */ -- cgit v1.2.3-59-g8ed1b From 09d86150141955ba1b3d7cbef23785f4996e4d6f Mon Sep 17 00:00:00 2001 From: Xu Yilun Date: Tue, 16 Jun 2020 12:08:47 +0800 Subject: fpga: dfl: afu: add AFU interrupt support AFU (Accelerated Function Unit) is dynamic region of the DFL based FPGA, and always defined by users. Some DFL based FPGA cards allow users to implement their own interrupts in AFU. In order to support this, hardware implements a new UINT (AFU Interrupt) private feature with related capability register which describes the number of supported AFU interrupts as well as the local index of the interrupts for software enumeration, and from software side, driver follows the common DFL interrupt notification and handling mechanism, and it implements two ioctls below for user to query number of irqs supported and set/unset interrupt triggers. Ioctls: * DFL_FPGA_PORT_UINT_GET_IRQ_NUM get the number of irqs, which is used to determine how many interrupts UINT feature supports. * DFL_FPGA_PORT_UINT_SET_IRQ set/unset eventfds as AFU interrupt triggers. Signed-off-by: Luwei Kang Signed-off-by: Wu Hao Signed-off-by: Xu Yilun Reviewed-by: Marcelo Tosatti Acked-by: Wu Hao Signed-off-by: Moritz Fischer --- drivers/fpga/dfl-afu-main.c | 28 ++++++++++++++++++++++++++++ include/uapi/linux/fpga-dfl.h | 23 +++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/drivers/fpga/dfl-afu-main.c b/drivers/fpga/dfl-afu-main.c index 357cd5d9b267..7c84feea7354 100644 --- a/drivers/fpga/dfl-afu-main.c +++ b/drivers/fpga/dfl-afu-main.c @@ -529,6 +529,30 @@ static const struct dfl_feature_ops port_stp_ops = { .init = port_stp_init, }; +static long +port_uint_ioctl(struct platform_device *pdev, struct dfl_feature *feature, + unsigned int cmd, unsigned long arg) +{ + switch (cmd) { + case DFL_FPGA_PORT_UINT_GET_IRQ_NUM: + return dfl_feature_ioctl_get_num_irqs(pdev, feature, arg); + case DFL_FPGA_PORT_UINT_SET_IRQ: + return dfl_feature_ioctl_set_irq(pdev, feature, arg); + default: + dev_dbg(&pdev->dev, "%x cmd not handled", cmd); + return -ENODEV; + } +} + +static const struct dfl_feature_id port_uint_id_table[] = { + {.id = PORT_FEATURE_ID_UINT,}, + {0,} +}; + +static const struct dfl_feature_ops port_uint_ops = { + .ioctl = port_uint_ioctl, +}; + static struct dfl_feature_driver port_feature_drvs[] = { { .id_table = port_hdr_id_table, @@ -546,6 +570,10 @@ static struct dfl_feature_driver port_feature_drvs[] = { .id_table = port_stp_id_table, .ops = &port_stp_ops, }, + { + .id_table = port_uint_id_table, + .ops = &port_uint_ops, + }, { .ops = NULL, } diff --git a/include/uapi/linux/fpga-dfl.h b/include/uapi/linux/fpga-dfl.h index b6495ea412ec..1621b077bf21 100644 --- a/include/uapi/linux/fpga-dfl.h +++ b/include/uapi/linux/fpga-dfl.h @@ -187,6 +187,29 @@ struct dfl_fpga_irq_set { DFL_PORT_BASE + 6, \ struct dfl_fpga_irq_set) +/** + * DFL_FPGA_PORT_UINT_GET_IRQ_NUM - _IOR(DFL_FPGA_MAGIC, DFL_PORT_BASE + 7, + * __u32 num_irqs) + * + * Get the number of irqs supported by the fpga AFU interrupt private + * feature. + * Return: 0 on success, -errno on failure. + */ +#define DFL_FPGA_PORT_UINT_GET_IRQ_NUM _IOR(DFL_FPGA_MAGIC, \ + DFL_PORT_BASE + 7, __u32) + +/** + * DFL_FPGA_PORT_UINT_SET_IRQ - _IOW(DFL_FPGA_MAGIC, DFL_PORT_BASE + 8, + * struct dfl_fpga_irq_set) + * + * Set fpga AFU interrupt trigger if evtfds[n] is valid. + * Unset related interrupt trigger if evtfds[n] is a negative value. + * Return: 0 on success, -errno on failure. + */ +#define DFL_FPGA_PORT_UINT_SET_IRQ _IOW(DFL_FPGA_MAGIC, \ + DFL_PORT_BASE + 8, \ + struct dfl_fpga_irq_set) + /* IOCTLs for FME file descriptor */ /** -- cgit v1.2.3-59-g8ed1b From 8adfb7c694d911669eb65256c760b3e250db1df5 Mon Sep 17 00:00:00 2001 From: Xu Yilun Date: Tue, 16 Jun 2020 12:08:48 +0800 Subject: Documentation: fpga: dfl: add descriptions for interrupt related interfaces. This patch adds introductions of interrupt related interfaces for FME error reporting, port error reporting and AFU user interrupts features. Signed-off-by: Luwei Kang Signed-off-by: Wu Hao Signed-off-by: Xu Yilun Reviewed-by: Marcelo Tosatti Acked-by: Wu Hao Signed-off-by: Moritz Fischer --- Documentation/fpga/dfl.rst | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Documentation/fpga/dfl.rst b/Documentation/fpga/dfl.rst index 978c4af416a4..2df9a0a0e981 100644 --- a/Documentation/fpga/dfl.rst +++ b/Documentation/fpga/dfl.rst @@ -89,6 +89,8 @@ The following functions are exposed through ioctls: - Program bitstream (DFL_FPGA_FME_PORT_PR) - Assign port to PF (DFL_FPGA_FME_PORT_ASSIGN) - Release port from PF (DFL_FPGA_FME_PORT_RELEASE) +- Get number of irqs of FME global error (DFL_FPGA_FME_ERR_GET_IRQ_NUM) +- Set interrupt trigger for FME error (DFL_FPGA_FME_ERR_SET_IRQ) More functions are exposed through sysfs (/sys/class/fpga_region/regionX/dfl-fme.n/): @@ -149,6 +151,10 @@ The following functions are exposed through ioctls: - Map DMA buffer (DFL_FPGA_PORT_DMA_MAP) - Unmap DMA buffer (DFL_FPGA_PORT_DMA_UNMAP) - Reset AFU (DFL_FPGA_PORT_RESET) +- Get number of irqs of port error (DFL_FPGA_PORT_ERR_GET_IRQ_NUM) +- Set interrupt trigger for port error (DFL_FPGA_PORT_ERR_SET_IRQ) +- Get number of irqs of UINT (DFL_FPGA_PORT_UINT_GET_IRQ_NUM) +- Set interrupt trigger for UINT (DFL_FPGA_PORT_UINT_SET_IRQ) DFL_FPGA_PORT_RESET: reset the FPGA Port and its AFU. Userspace can do Port @@ -462,6 +468,19 @@ since they are system-wide counters on FPGA device. The current driver does not support sampling. So "perf record" is unsupported. +Interrupt support +================= +Some FME and AFU private features are able to generate interrupts. As mentioned +above, users could call ioctl (DFL_FPGA_*_GET_IRQ_NUM) to know whether or how +many interrupts are supported for this private feature. Drivers also implement +an eventfd based interrupt handling mechanism for users to get notified when +interrupt happens. Users could set eventfds to driver via +ioctl (DFL_FPGA_*_SET_IRQ), and then poll/select on these eventfds waiting for +notification. +In Current DFL, 3 sub features (Port error, FME global error and AFU interrupt) +support interrupts. + + Add new FIUs support ==================== It's possible that developers made some new function blocks (FIUs) under this -- cgit v1.2.3-59-g8ed1b From 02dca8c981b552ad4caaa2e858800c78cc20f41c Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Mon, 29 Jun 2020 20:24:52 +0530 Subject: phy: qcom: remove ufs qmp phy driver The UFS specific QMP PHY driver started off supporting the 14nm and 20nm hardware. With the 20nm support marked broken for a long time and the 14nm support added to the common QMP PHY, this driver has not been used in a while. So delete it Reviewed-by: Bjorn Andersson Link: https://lore.kernel.org/r/20200629145452.123035-1-vkoul@kernel.org Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/Kconfig | 24 - drivers/phy/qualcomm/Makefile | 3 - drivers/phy/qualcomm/phy-qcom-ufs-i.h | 131 ------ drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c | 172 ------- drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.h | 168 ------- drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c | 226 ---------- drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.h | 226 ---------- drivers/phy/qualcomm/phy-qcom-ufs.c | 648 --------------------------- 8 files changed, 1598 deletions(-) delete mode 100644 drivers/phy/qualcomm/phy-qcom-ufs-i.h delete mode 100644 drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c delete mode 100644 drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.h delete mode 100644 drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c delete mode 100644 drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.h delete mode 100644 drivers/phy/qualcomm/phy-qcom-ufs.c diff --git a/drivers/phy/qualcomm/Kconfig b/drivers/phy/qualcomm/Kconfig index ca9ce7e84a5c..e6f0e514fb1f 100644 --- a/drivers/phy/qualcomm/Kconfig +++ b/drivers/phy/qualcomm/Kconfig @@ -59,30 +59,6 @@ config PHY_QCOM_QUSB2 PHY which is usually paired with either the ChipIdea or Synopsys DWC3 USB IPs on MSM SOCs. -config PHY_QCOM_UFS - tristate "Qualcomm UFS PHY driver" - depends on OF && ARCH_QCOM - select GENERIC_PHY - help - Support for UFS PHY on QCOM chipsets. - -if PHY_QCOM_UFS - -config PHY_QCOM_UFS_14NM - tristate - default PHY_QCOM_UFS - help - Support for 14nm UFS QMP phy present on QCOM chipsets. - -config PHY_QCOM_UFS_20NM - tristate - default PHY_QCOM_UFS - depends on BROKEN - help - Support for 20nm UFS QMP phy present on QCOM chipsets. - -endif - config PHY_QCOM_USB_HS tristate "Qualcomm USB HS PHY module" depends on USB_ULPI_BUS diff --git a/drivers/phy/qualcomm/Makefile b/drivers/phy/qualcomm/Makefile index 86fb32efab79..a767915decd0 100644 --- a/drivers/phy/qualcomm/Makefile +++ b/drivers/phy/qualcomm/Makefile @@ -6,9 +6,6 @@ obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA) += phy-qcom-ipq806x-sata.o obj-$(CONFIG_PHY_QCOM_PCIE2) += phy-qcom-pcie2.o obj-$(CONFIG_PHY_QCOM_QMP) += phy-qcom-qmp.o obj-$(CONFIG_PHY_QCOM_QUSB2) += phy-qcom-qusb2.o -obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs.o -obj-$(CONFIG_PHY_QCOM_UFS_14NM) += phy-qcom-ufs-qmp-14nm.o -obj-$(CONFIG_PHY_QCOM_UFS_20NM) += phy-qcom-ufs-qmp-20nm.o obj-$(CONFIG_PHY_QCOM_USB_HS) += phy-qcom-usb-hs.o obj-$(CONFIG_PHY_QCOM_USB_HSIC) += phy-qcom-usb-hsic.o obj-$(CONFIG_PHY_QCOM_USB_HS_28NM) += phy-qcom-usb-hs-28nm.o diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-i.h b/drivers/phy/qualcomm/phy-qcom-ufs-i.h deleted file mode 100644 index 9bf973a0d8c3..000000000000 --- a/drivers/phy/qualcomm/phy-qcom-ufs-i.h +++ /dev/null @@ -1,131 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (c) 2013-2015, Linux Foundation. All rights reserved. - */ - -#ifndef UFS_QCOM_PHY_I_H_ -#define UFS_QCOM_PHY_I_H_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define UFS_QCOM_PHY_CAL_ENTRY(reg, val) \ - { \ - .reg_offset = reg, \ - .cfg_value = val, \ - } - -#define UFS_QCOM_PHY_NAME_LEN 30 - -enum { - MASK_SERDES_START = 0x1, - MASK_PCS_READY = 0x1, -}; - -enum { - OFFSET_SERDES_START = 0x0, -}; - -struct ufs_qcom_phy_stored_attributes { - u32 att; - u32 value; -}; - - -struct ufs_qcom_phy_calibration { - u32 reg_offset; - u32 cfg_value; -}; - -struct ufs_qcom_phy_vreg { - const char *name; - struct regulator *reg; - int max_uA; - int min_uV; - int max_uV; - bool enabled; -}; - -struct ufs_qcom_phy { - struct list_head list; - struct device *dev; - void __iomem *mmio; - void __iomem *dev_ref_clk_ctrl_mmio; - struct clk *tx_iface_clk; - struct clk *rx_iface_clk; - bool is_iface_clk_enabled; - struct clk *ref_clk_src; - struct clk *ref_clk_parent; - struct clk *ref_clk; - bool is_ref_clk_enabled; - bool is_dev_ref_clk_enabled; - struct ufs_qcom_phy_vreg vdda_pll; - struct ufs_qcom_phy_vreg vdda_phy; - struct ufs_qcom_phy_vreg vddp_ref_clk; - unsigned int quirks; - - /* - * If UFS link is put into Hibern8 and if UFS PHY analog hardware is - * power collapsed (by clearing UFS_PHY_POWER_DOWN_CONTROL), Hibern8 - * exit might fail even after powering on UFS PHY analog hardware. - * Enabling this quirk will help to solve above issue by doing - * custom PHY settings just before PHY analog power collapse. - */ - #define UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE BIT(0) - - u8 host_ctrl_rev_major; - u16 host_ctrl_rev_minor; - u16 host_ctrl_rev_step; - - char name[UFS_QCOM_PHY_NAME_LEN]; - struct ufs_qcom_phy_calibration *cached_regs; - int cached_regs_table_size; - struct ufs_qcom_phy_specific_ops *phy_spec_ops; - - enum phy_mode mode; - struct reset_control *ufs_reset; -}; - -/** - * struct ufs_qcom_phy_specific_ops - set of pointers to functions which have a - * specific implementation per phy. Each UFS phy, should implement - * those functions according to its spec and requirements - * @start_serdes: pointer to a function that starts the serdes - * @is_physical_coding_sublayer_ready: pointer to a function that - * checks pcs readiness. returns 0 for success and non-zero for error. - * @set_tx_lane_enable: pointer to a function that enable tx lanes - * @power_control: pointer to a function that controls analog rail of phy - * and writes to QSERDES_RX_SIGDET_CNTRL attribute - */ -struct ufs_qcom_phy_specific_ops { - int (*calibrate)(struct ufs_qcom_phy *ufs_qcom_phy, bool is_rate_B); - void (*start_serdes)(struct ufs_qcom_phy *phy); - int (*is_physical_coding_sublayer_ready)(struct ufs_qcom_phy *phy); - void (*set_tx_lane_enable)(struct ufs_qcom_phy *phy, u32 val); - void (*power_control)(struct ufs_qcom_phy *phy, bool val); -}; - -struct ufs_qcom_phy *get_ufs_qcom_phy(struct phy *generic_phy); -int ufs_qcom_phy_power_on(struct phy *generic_phy); -int ufs_qcom_phy_power_off(struct phy *generic_phy); -int ufs_qcom_phy_init_clks(struct ufs_qcom_phy *phy_common); -int ufs_qcom_phy_init_vregulators(struct ufs_qcom_phy *phy_common); -int ufs_qcom_phy_remove(struct phy *generic_phy, - struct ufs_qcom_phy *ufs_qcom_phy); -struct phy *ufs_qcom_phy_generic_probe(struct platform_device *pdev, - struct ufs_qcom_phy *common_cfg, - const struct phy_ops *ufs_qcom_phy_gen_ops, - struct ufs_qcom_phy_specific_ops *phy_spec_ops); -int ufs_qcom_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, - struct ufs_qcom_phy_calibration *tbl_A, int tbl_size_A, - struct ufs_qcom_phy_calibration *tbl_B, int tbl_size_B, - bool is_rate_B); -#endif diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c deleted file mode 100644 index 54b355bfc24c..000000000000 --- a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c +++ /dev/null @@ -1,172 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (c) 2013-2015, Linux Foundation. All rights reserved. - */ - -#include "phy-qcom-ufs-qmp-14nm.h" - -#define UFS_PHY_NAME "ufs_phy_qmp_14nm" -#define UFS_PHY_VDDA_PHY_UV (925000) - -static -int ufs_qcom_phy_qmp_14nm_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, - bool is_rate_B) -{ - int tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A); - int tbl_size_B = ARRAY_SIZE(phy_cal_table_rate_B); - int err; - - err = ufs_qcom_phy_calibrate(ufs_qcom_phy, phy_cal_table_rate_A, - tbl_size_A, phy_cal_table_rate_B, tbl_size_B, is_rate_B); - - if (err) - dev_err(ufs_qcom_phy->dev, - "%s: ufs_qcom_phy_calibrate() failed %d\n", - __func__, err); - return err; -} - -static -void ufs_qcom_phy_qmp_14nm_advertise_quirks(struct ufs_qcom_phy *phy_common) -{ - phy_common->quirks = - UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE; -} - -static -int ufs_qcom_phy_qmp_14nm_set_mode(struct phy *generic_phy, - enum phy_mode mode, int submode) -{ - struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy); - - phy_common->mode = PHY_MODE_INVALID; - - if (mode > 0) - phy_common->mode = mode; - - return 0; -} - -static -void ufs_qcom_phy_qmp_14nm_power_control(struct ufs_qcom_phy *phy, bool val) -{ - writel_relaxed(val ? 0x1 : 0x0, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL); - /* - * Before any transactions involving PHY, ensure PHY knows - * that it's analog rail is powered ON (or OFF). - */ - mb(); -} - -static inline -void ufs_qcom_phy_qmp_14nm_set_tx_lane_enable(struct ufs_qcom_phy *phy, u32 val) -{ - /* - * 14nm PHY does not have TX_LANE_ENABLE register. - * Implement this function so as not to propagate error to caller. - */ -} - -static inline void ufs_qcom_phy_qmp_14nm_start_serdes(struct ufs_qcom_phy *phy) -{ - u32 tmp; - - tmp = readl_relaxed(phy->mmio + UFS_PHY_PHY_START); - tmp &= ~MASK_SERDES_START; - tmp |= (1 << OFFSET_SERDES_START); - writel_relaxed(tmp, phy->mmio + UFS_PHY_PHY_START); - /* Ensure register value is committed */ - mb(); -} - -static int ufs_qcom_phy_qmp_14nm_is_pcs_ready(struct ufs_qcom_phy *phy_common) -{ - int err = 0; - u32 val; - - err = readl_poll_timeout(phy_common->mmio + UFS_PHY_PCS_READY_STATUS, - val, (val & MASK_PCS_READY), 10, 1000000); - if (err) - dev_err(phy_common->dev, "%s: poll for pcs failed err = %d\n", - __func__, err); - return err; -} - -static const struct phy_ops ufs_qcom_phy_qmp_14nm_phy_ops = { - .power_on = ufs_qcom_phy_power_on, - .power_off = ufs_qcom_phy_power_off, - .set_mode = ufs_qcom_phy_qmp_14nm_set_mode, - .owner = THIS_MODULE, -}; - -static struct ufs_qcom_phy_specific_ops phy_14nm_ops = { - .calibrate = ufs_qcom_phy_qmp_14nm_phy_calibrate, - .start_serdes = ufs_qcom_phy_qmp_14nm_start_serdes, - .is_physical_coding_sublayer_ready = ufs_qcom_phy_qmp_14nm_is_pcs_ready, - .set_tx_lane_enable = ufs_qcom_phy_qmp_14nm_set_tx_lane_enable, - .power_control = ufs_qcom_phy_qmp_14nm_power_control, -}; - -static int ufs_qcom_phy_qmp_14nm_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct phy *generic_phy; - struct ufs_qcom_phy_qmp_14nm *phy; - struct ufs_qcom_phy *phy_common; - int err = 0; - - phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); - if (!phy) { - err = -ENOMEM; - goto out; - } - phy_common = &phy->common_cfg; - - generic_phy = ufs_qcom_phy_generic_probe(pdev, phy_common, - &ufs_qcom_phy_qmp_14nm_phy_ops, &phy_14nm_ops); - - if (!generic_phy) { - err = -EIO; - goto out; - } - - err = ufs_qcom_phy_init_clks(phy_common); - if (err) - goto out; - - err = ufs_qcom_phy_init_vregulators(phy_common); - if (err) - goto out; - - phy_common->vdda_phy.max_uV = UFS_PHY_VDDA_PHY_UV; - phy_common->vdda_phy.min_uV = UFS_PHY_VDDA_PHY_UV; - - ufs_qcom_phy_qmp_14nm_advertise_quirks(phy_common); - - phy_set_drvdata(generic_phy, phy); - - strlcpy(phy_common->name, UFS_PHY_NAME, sizeof(phy_common->name)); - -out: - return err; -} - -static const struct of_device_id ufs_qcom_phy_qmp_14nm_of_match[] = { - {.compatible = "qcom,ufs-phy-qmp-14nm"}, - {.compatible = "qcom,msm8996-ufs-phy-qmp-14nm"}, - {}, -}; -MODULE_DEVICE_TABLE(of, ufs_qcom_phy_qmp_14nm_of_match); - -static struct platform_driver ufs_qcom_phy_qmp_14nm_driver = { - .probe = ufs_qcom_phy_qmp_14nm_probe, - .driver = { - .of_match_table = ufs_qcom_phy_qmp_14nm_of_match, - .name = "ufs_qcom_phy_qmp_14nm", - }, -}; - -module_platform_driver(ufs_qcom_phy_qmp_14nm_driver); - -MODULE_DESCRIPTION("Universal Flash Storage (UFS) QCOM PHY QMP 14nm"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.h b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.h deleted file mode 100644 index ceca654b9338..000000000000 --- a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.h +++ /dev/null @@ -1,168 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (c) 2013-2015, Linux Foundation. All rights reserved. - */ - -#ifndef UFS_QCOM_PHY_QMP_14NM_H_ -#define UFS_QCOM_PHY_QMP_14NM_H_ - -#include "phy-qcom-ufs-i.h" - -/* QCOM UFS PHY control registers */ -#define COM_OFF(x) (0x000 + x) -#define PHY_OFF(x) (0xC00 + x) -#define TX_OFF(n, x) (0x400 + (0x400 * n) + x) -#define RX_OFF(n, x) (0x600 + (0x400 * n) + x) - -/* UFS PHY QSERDES COM registers */ -#define QSERDES_COM_BG_TIMER COM_OFF(0x0C) -#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN COM_OFF(0x34) -#define QSERDES_COM_SYS_CLK_CTRL COM_OFF(0x3C) -#define QSERDES_COM_LOCK_CMP1_MODE0 COM_OFF(0x4C) -#define QSERDES_COM_LOCK_CMP2_MODE0 COM_OFF(0x50) -#define QSERDES_COM_LOCK_CMP3_MODE0 COM_OFF(0x54) -#define QSERDES_COM_LOCK_CMP1_MODE1 COM_OFF(0x58) -#define QSERDES_COM_LOCK_CMP2_MODE1 COM_OFF(0x5C) -#define QSERDES_COM_LOCK_CMP3_MODE1 COM_OFF(0x60) -#define QSERDES_COM_CP_CTRL_MODE0 COM_OFF(0x78) -#define QSERDES_COM_CP_CTRL_MODE1 COM_OFF(0x7C) -#define QSERDES_COM_PLL_RCTRL_MODE0 COM_OFF(0x84) -#define QSERDES_COM_PLL_RCTRL_MODE1 COM_OFF(0x88) -#define QSERDES_COM_PLL_CCTRL_MODE0 COM_OFF(0x90) -#define QSERDES_COM_PLL_CCTRL_MODE1 COM_OFF(0x94) -#define QSERDES_COM_SYSCLK_EN_SEL COM_OFF(0xAC) -#define QSERDES_COM_RESETSM_CNTRL COM_OFF(0xB4) -#define QSERDES_COM_LOCK_CMP_EN COM_OFF(0xC8) -#define QSERDES_COM_LOCK_CMP_CFG COM_OFF(0xCC) -#define QSERDES_COM_DEC_START_MODE0 COM_OFF(0xD0) -#define QSERDES_COM_DEC_START_MODE1 COM_OFF(0xD4) -#define QSERDES_COM_DIV_FRAC_START1_MODE0 COM_OFF(0xDC) -#define QSERDES_COM_DIV_FRAC_START2_MODE0 COM_OFF(0xE0) -#define QSERDES_COM_DIV_FRAC_START3_MODE0 COM_OFF(0xE4) -#define QSERDES_COM_DIV_FRAC_START1_MODE1 COM_OFF(0xE8) -#define QSERDES_COM_DIV_FRAC_START2_MODE1 COM_OFF(0xEC) -#define QSERDES_COM_DIV_FRAC_START3_MODE1 COM_OFF(0xF0) -#define QSERDES_COM_INTEGLOOP_GAIN0_MODE0 COM_OFF(0x108) -#define QSERDES_COM_INTEGLOOP_GAIN1_MODE0 COM_OFF(0x10C) -#define QSERDES_COM_INTEGLOOP_GAIN0_MODE1 COM_OFF(0x110) -#define QSERDES_COM_INTEGLOOP_GAIN1_MODE1 COM_OFF(0x114) -#define QSERDES_COM_VCO_TUNE_CTRL COM_OFF(0x124) -#define QSERDES_COM_VCO_TUNE_MAP COM_OFF(0x128) -#define QSERDES_COM_VCO_TUNE1_MODE0 COM_OFF(0x12C) -#define QSERDES_COM_VCO_TUNE2_MODE0 COM_OFF(0x130) -#define QSERDES_COM_VCO_TUNE1_MODE1 COM_OFF(0x134) -#define QSERDES_COM_VCO_TUNE2_MODE1 COM_OFF(0x138) -#define QSERDES_COM_VCO_TUNE_TIMER1 COM_OFF(0x144) -#define QSERDES_COM_VCO_TUNE_TIMER2 COM_OFF(0x148) -#define QSERDES_COM_CLK_SELECT COM_OFF(0x174) -#define QSERDES_COM_HSCLK_SEL COM_OFF(0x178) -#define QSERDES_COM_CORECLK_DIV COM_OFF(0x184) -#define QSERDES_COM_CORE_CLK_EN COM_OFF(0x18C) -#define QSERDES_COM_CMN_CONFIG COM_OFF(0x194) -#define QSERDES_COM_SVS_MODE_CLK_SEL COM_OFF(0x19C) -#define QSERDES_COM_CORECLK_DIV_MODE1 COM_OFF(0x1BC) - -/* UFS PHY registers */ -#define UFS_PHY_PHY_START PHY_OFF(0x00) -#define UFS_PHY_POWER_DOWN_CONTROL PHY_OFF(0x04) -#define UFS_PHY_PCS_READY_STATUS PHY_OFF(0x168) - -/* UFS PHY TX registers */ -#define QSERDES_TX_HIGHZ_TRANSCEIVER_BIAS_DRVR_EN TX_OFF(0, 0x68) -#define QSERDES_TX_LANE_MODE TX_OFF(0, 0x94) - -/* UFS PHY RX registers */ -#define QSERDES_RX_UCDR_FASTLOCK_FO_GAIN RX_OFF(0, 0x40) -#define QSERDES_RX_RX_TERM_BW RX_OFF(0, 0x90) -#define QSERDES_RX_RX_EQ_GAIN1_LSB RX_OFF(0, 0xC4) -#define QSERDES_RX_RX_EQ_GAIN1_MSB RX_OFF(0, 0xC8) -#define QSERDES_RX_RX_EQ_GAIN2_LSB RX_OFF(0, 0xCC) -#define QSERDES_RX_RX_EQ_GAIN2_MSB RX_OFF(0, 0xD0) -#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2 RX_OFF(0, 0xD8) -#define QSERDES_RX_SIGDET_CNTRL RX_OFF(0, 0x114) -#define QSERDES_RX_SIGDET_LVL RX_OFF(0, 0x118) -#define QSERDES_RX_SIGDET_DEGLITCH_CNTRL RX_OFF(0, 0x11C) -#define QSERDES_RX_RX_INTERFACE_MODE RX_OFF(0, 0x12C) - -/* - * This structure represents the 14nm specific phy. - * common_cfg MUST remain the first field in this structure - * in case extra fields are added. This way, when calling - * get_ufs_qcom_phy() of generic phy, we can extract the - * common phy structure (struct ufs_qcom_phy) out of it - * regardless of the relevant specific phy. - */ -struct ufs_qcom_phy_qmp_14nm { - struct ufs_qcom_phy common_cfg; -}; - -static struct ufs_qcom_phy_calibration phy_cal_table_rate_A[] = { - UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_POWER_DOWN_CONTROL, 0x01), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CMN_CONFIG, 0x0e), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYSCLK_EN_SEL, 0xd7), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CLK_SELECT, 0x30), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYS_CLK_CTRL, 0x06), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x08), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BG_TIMER, 0x0a), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_HSCLK_SEL, 0x05), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CORECLK_DIV, 0x0a), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CORECLK_DIV_MODE1, 0x0a), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP_EN, 0x01), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_CTRL, 0x10), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL, 0x20), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CORE_CLK_EN, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP_CFG, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_TIMER1, 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_TIMER2, 0x3f), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_MAP, 0x14), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SVS_MODE_CLK_SEL, 0x05), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START_MODE0, 0x82), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CP_CTRL_MODE0, 0x0b), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RCTRL_MODE0, 0x16), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CCTRL_MODE0, 0x28), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE1_MODE0, 0x28), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE2_MODE0, 0x02), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP1_MODE0, 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP2_MODE0, 0x0c), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP3_MODE0, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START_MODE1, 0x98), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START1_MODE1, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START2_MODE1, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START3_MODE1, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CP_CTRL_MODE1, 0x0b), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RCTRL_MODE1, 0x16), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CCTRL_MODE1, 0x28), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN0_MODE1, 0x80), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN1_MODE1, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE1_MODE1, 0xd6), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE2_MODE1, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP1_MODE1, 0x32), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP2_MODE1, 0x0f), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP3_MODE1, 0x00), - - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_HIGHZ_TRANSCEIVER_BIAS_DRVR_EN, 0x45), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE, 0x02), - - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_LVL, 0x24), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_CNTRL, 0x02), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_INTERFACE_MODE, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x18), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_UCDR_FASTLOCK_FO_GAIN, 0x0B), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_TERM_BW, 0x5B), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB, 0xFF), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB, 0x3F), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB, 0xFF), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB, 0x0F), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0E), -}; - -static struct ufs_qcom_phy_calibration phy_cal_table_rate_B[] = { - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_MAP, 0x54), -}; - -#endif diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c deleted file mode 100644 index 3e9d8b71e995..000000000000 --- a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c +++ /dev/null @@ -1,226 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (c) 2013-2015, Linux Foundation. All rights reserved. - */ - -#include "phy-qcom-ufs-qmp-20nm.h" - -#define UFS_PHY_NAME "ufs_phy_qmp_20nm" - -static -int ufs_qcom_phy_qmp_20nm_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, - bool is_rate_B) -{ - struct ufs_qcom_phy_calibration *tbl_A, *tbl_B; - int tbl_size_A, tbl_size_B; - u8 major = ufs_qcom_phy->host_ctrl_rev_major; - u16 minor = ufs_qcom_phy->host_ctrl_rev_minor; - u16 step = ufs_qcom_phy->host_ctrl_rev_step; - int err; - - if ((major == 0x1) && (minor == 0x002) && (step == 0x0000)) { - tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A_1_2_0); - tbl_A = phy_cal_table_rate_A_1_2_0; - } else if ((major == 0x1) && (minor == 0x003) && (step == 0x0000)) { - tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A_1_3_0); - tbl_A = phy_cal_table_rate_A_1_3_0; - } else { - dev_err(ufs_qcom_phy->dev, "%s: Unknown UFS-PHY version, no calibration values\n", - __func__); - err = -ENODEV; - goto out; - } - - tbl_size_B = ARRAY_SIZE(phy_cal_table_rate_B); - tbl_B = phy_cal_table_rate_B; - - err = ufs_qcom_phy_calibrate(ufs_qcom_phy, tbl_A, tbl_size_A, - tbl_B, tbl_size_B, is_rate_B); - - if (err) - dev_err(ufs_qcom_phy->dev, "%s: ufs_qcom_phy_calibrate() failed %d\n", - __func__, err); - -out: - return err; -} - -static -void ufs_qcom_phy_qmp_20nm_advertise_quirks(struct ufs_qcom_phy *phy_common) -{ - phy_common->quirks = - UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE; -} - -static -int ufs_qcom_phy_qmp_20nm_set_mode(struct phy *generic_phy, - enum phy_mode mode, int submode) -{ - struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy); - - phy_common->mode = PHY_MODE_INVALID; - - if (mode > 0) - phy_common->mode = mode; - - return 0; -} - -static -void ufs_qcom_phy_qmp_20nm_power_control(struct ufs_qcom_phy *phy, bool val) -{ - bool hibern8_exit_after_pwr_collapse = phy->quirks & - UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE; - - if (val) { - writel_relaxed(0x1, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL); - /* - * Before any transactions involving PHY, ensure PHY knows - * that it's analog rail is powered ON. - */ - mb(); - - if (hibern8_exit_after_pwr_collapse) { - /* - * Give atleast 1us delay after restoring PHY analog - * power. - */ - usleep_range(1, 2); - writel_relaxed(0x0A, phy->mmio + - QSERDES_COM_SYSCLK_EN_SEL_TXBAND); - writel_relaxed(0x08, phy->mmio + - QSERDES_COM_SYSCLK_EN_SEL_TXBAND); - /* - * Make sure workaround is deactivated before proceeding - * with normal PHY operations. - */ - mb(); - } - } else { - if (hibern8_exit_after_pwr_collapse) { - writel_relaxed(0x0A, phy->mmio + - QSERDES_COM_SYSCLK_EN_SEL_TXBAND); - writel_relaxed(0x02, phy->mmio + - QSERDES_COM_SYSCLK_EN_SEL_TXBAND); - /* - * Make sure that above workaround is activated before - * PHY analog power collapse. - */ - mb(); - } - - writel_relaxed(0x0, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL); - /* - * ensure that PHY knows its PHY analog rail is going - * to be powered down - */ - mb(); - } -} - -static -void ufs_qcom_phy_qmp_20nm_set_tx_lane_enable(struct ufs_qcom_phy *phy, u32 val) -{ - writel_relaxed(val & UFS_PHY_TX_LANE_ENABLE_MASK, - phy->mmio + UFS_PHY_TX_LANE_ENABLE); - mb(); -} - -static inline void ufs_qcom_phy_qmp_20nm_start_serdes(struct ufs_qcom_phy *phy) -{ - u32 tmp; - - tmp = readl_relaxed(phy->mmio + UFS_PHY_PHY_START); - tmp &= ~MASK_SERDES_START; - tmp |= (1 << OFFSET_SERDES_START); - writel_relaxed(tmp, phy->mmio + UFS_PHY_PHY_START); - mb(); -} - -static int ufs_qcom_phy_qmp_20nm_is_pcs_ready(struct ufs_qcom_phy *phy_common) -{ - int err = 0; - u32 val; - - err = readl_poll_timeout(phy_common->mmio + UFS_PHY_PCS_READY_STATUS, - val, (val & MASK_PCS_READY), 10, 1000000); - if (err) - dev_err(phy_common->dev, "%s: poll for pcs failed err = %d\n", - __func__, err); - return err; -} - -static const struct phy_ops ufs_qcom_phy_qmp_20nm_phy_ops = { - .power_on = ufs_qcom_phy_power_on, - .power_off = ufs_qcom_phy_power_off, - .set_mode = ufs_qcom_phy_qmp_20nm_set_mode, - .owner = THIS_MODULE, -}; - -static struct ufs_qcom_phy_specific_ops phy_20nm_ops = { - .calibrate = ufs_qcom_phy_qmp_20nm_phy_calibrate, - .start_serdes = ufs_qcom_phy_qmp_20nm_start_serdes, - .is_physical_coding_sublayer_ready = ufs_qcom_phy_qmp_20nm_is_pcs_ready, - .set_tx_lane_enable = ufs_qcom_phy_qmp_20nm_set_tx_lane_enable, - .power_control = ufs_qcom_phy_qmp_20nm_power_control, -}; - -static int ufs_qcom_phy_qmp_20nm_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct phy *generic_phy; - struct ufs_qcom_phy_qmp_20nm *phy; - struct ufs_qcom_phy *phy_common; - int err = 0; - - phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); - if (!phy) { - err = -ENOMEM; - goto out; - } - phy_common = &phy->common_cfg; - - generic_phy = ufs_qcom_phy_generic_probe(pdev, phy_common, - &ufs_qcom_phy_qmp_20nm_phy_ops, &phy_20nm_ops); - - if (!generic_phy) { - err = -EIO; - goto out; - } - - err = ufs_qcom_phy_init_clks(phy_common); - if (err) - goto out; - - err = ufs_qcom_phy_init_vregulators(phy_common); - if (err) - goto out; - - ufs_qcom_phy_qmp_20nm_advertise_quirks(phy_common); - - phy_set_drvdata(generic_phy, phy); - - strlcpy(phy_common->name, UFS_PHY_NAME, sizeof(phy_common->name)); - -out: - return err; -} - -static const struct of_device_id ufs_qcom_phy_qmp_20nm_of_match[] = { - {.compatible = "qcom,ufs-phy-qmp-20nm"}, - {}, -}; -MODULE_DEVICE_TABLE(of, ufs_qcom_phy_qmp_20nm_of_match); - -static struct platform_driver ufs_qcom_phy_qmp_20nm_driver = { - .probe = ufs_qcom_phy_qmp_20nm_probe, - .driver = { - .of_match_table = ufs_qcom_phy_qmp_20nm_of_match, - .name = "ufs_qcom_phy_qmp_20nm", - }, -}; - -module_platform_driver(ufs_qcom_phy_qmp_20nm_driver); - -MODULE_DESCRIPTION("Universal Flash Storage (UFS) QCOM PHY QMP 20nm"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.h b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.h deleted file mode 100644 index 8ce79f524eed..000000000000 --- a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.h +++ /dev/null @@ -1,226 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (c) 2013-2015, Linux Foundation. All rights reserved. - */ - -#ifndef UFS_QCOM_PHY_QMP_20NM_H_ -#define UFS_QCOM_PHY_QMP_20NM_H_ - -#include "phy-qcom-ufs-i.h" - -/* QCOM UFS PHY control registers */ - -#define COM_OFF(x) (0x000 + x) -#define PHY_OFF(x) (0xC00 + x) -#define TX_OFF(n, x) (0x400 + (0x400 * n) + x) -#define RX_OFF(n, x) (0x600 + (0x400 * n) + x) - -/* UFS PHY PLL block registers */ -#define QSERDES_COM_SYS_CLK_CTRL COM_OFF(0x0) -#define QSERDES_COM_PLL_VCOTAIL_EN COM_OFF(0x04) -#define QSERDES_COM_PLL_CNTRL COM_OFF(0x14) -#define QSERDES_COM_PLL_IP_SETI COM_OFF(0x24) -#define QSERDES_COM_CORE_CLK_IN_SYNC_SEL COM_OFF(0x28) -#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN COM_OFF(0x30) -#define QSERDES_COM_PLL_CP_SETI COM_OFF(0x34) -#define QSERDES_COM_PLL_IP_SETP COM_OFF(0x38) -#define QSERDES_COM_PLL_CP_SETP COM_OFF(0x3C) -#define QSERDES_COM_SYSCLK_EN_SEL_TXBAND COM_OFF(0x48) -#define QSERDES_COM_RESETSM_CNTRL COM_OFF(0x4C) -#define QSERDES_COM_RESETSM_CNTRL2 COM_OFF(0x50) -#define QSERDES_COM_PLLLOCK_CMP1 COM_OFF(0x90) -#define QSERDES_COM_PLLLOCK_CMP2 COM_OFF(0x94) -#define QSERDES_COM_PLLLOCK_CMP3 COM_OFF(0x98) -#define QSERDES_COM_PLLLOCK_CMP_EN COM_OFF(0x9C) -#define QSERDES_COM_BGTC COM_OFF(0xA0) -#define QSERDES_COM_DEC_START1 COM_OFF(0xAC) -#define QSERDES_COM_PLL_AMP_OS COM_OFF(0xB0) -#define QSERDES_COM_RES_CODE_UP_OFFSET COM_OFF(0xD8) -#define QSERDES_COM_RES_CODE_DN_OFFSET COM_OFF(0xDC) -#define QSERDES_COM_DIV_FRAC_START1 COM_OFF(0x100) -#define QSERDES_COM_DIV_FRAC_START2 COM_OFF(0x104) -#define QSERDES_COM_DIV_FRAC_START3 COM_OFF(0x108) -#define QSERDES_COM_DEC_START2 COM_OFF(0x10C) -#define QSERDES_COM_PLL_RXTXEPCLK_EN COM_OFF(0x110) -#define QSERDES_COM_PLL_CRCTRL COM_OFF(0x114) -#define QSERDES_COM_PLL_CLKEPDIV COM_OFF(0x118) - -/* TX LANE n (0, 1) registers */ -#define QSERDES_TX_EMP_POST1_LVL(n) TX_OFF(n, 0x08) -#define QSERDES_TX_DRV_LVL(n) TX_OFF(n, 0x0C) -#define QSERDES_TX_LANE_MODE(n) TX_OFF(n, 0x54) - -/* RX LANE n (0, 1) registers */ -#define QSERDES_RX_CDR_CONTROL1(n) RX_OFF(n, 0x0) -#define QSERDES_RX_CDR_CONTROL_HALF(n) RX_OFF(n, 0x8) -#define QSERDES_RX_RX_EQ_GAIN1_LSB(n) RX_OFF(n, 0xA8) -#define QSERDES_RX_RX_EQ_GAIN1_MSB(n) RX_OFF(n, 0xAC) -#define QSERDES_RX_RX_EQ_GAIN2_LSB(n) RX_OFF(n, 0xB0) -#define QSERDES_RX_RX_EQ_GAIN2_MSB(n) RX_OFF(n, 0xB4) -#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2(n) RX_OFF(n, 0xBC) -#define QSERDES_RX_CDR_CONTROL_QUARTER(n) RX_OFF(n, 0xC) -#define QSERDES_RX_SIGDET_CNTRL(n) RX_OFF(n, 0x100) - -/* UFS PHY registers */ -#define UFS_PHY_PHY_START PHY_OFF(0x00) -#define UFS_PHY_POWER_DOWN_CONTROL PHY_OFF(0x4) -#define UFS_PHY_TX_LANE_ENABLE PHY_OFF(0x44) -#define UFS_PHY_PWM_G1_CLK_DIVIDER PHY_OFF(0x08) -#define UFS_PHY_PWM_G2_CLK_DIVIDER PHY_OFF(0x0C) -#define UFS_PHY_PWM_G3_CLK_DIVIDER PHY_OFF(0x10) -#define UFS_PHY_PWM_G4_CLK_DIVIDER PHY_OFF(0x14) -#define UFS_PHY_CORECLK_PWM_G1_CLK_DIVIDER PHY_OFF(0x34) -#define UFS_PHY_CORECLK_PWM_G2_CLK_DIVIDER PHY_OFF(0x38) -#define UFS_PHY_CORECLK_PWM_G3_CLK_DIVIDER PHY_OFF(0x3C) -#define UFS_PHY_CORECLK_PWM_G4_CLK_DIVIDER PHY_OFF(0x40) -#define UFS_PHY_OMC_STATUS_RDVAL PHY_OFF(0x68) -#define UFS_PHY_LINE_RESET_TIME PHY_OFF(0x28) -#define UFS_PHY_LINE_RESET_GRANULARITY PHY_OFF(0x2C) -#define UFS_PHY_TSYNC_RSYNC_CNTL PHY_OFF(0x48) -#define UFS_PHY_PLL_CNTL PHY_OFF(0x50) -#define UFS_PHY_TX_LARGE_AMP_DRV_LVL PHY_OFF(0x54) -#define UFS_PHY_TX_SMALL_AMP_DRV_LVL PHY_OFF(0x5C) -#define UFS_PHY_TX_LARGE_AMP_POST_EMP_LVL PHY_OFF(0x58) -#define UFS_PHY_TX_SMALL_AMP_POST_EMP_LVL PHY_OFF(0x60) -#define UFS_PHY_CFG_CHANGE_CNT_VAL PHY_OFF(0x64) -#define UFS_PHY_RX_SYNC_WAIT_TIME PHY_OFF(0x6C) -#define UFS_PHY_TX_MIN_SLEEP_NOCONFIG_TIME_CAPABILITY PHY_OFF(0xB4) -#define UFS_PHY_RX_MIN_SLEEP_NOCONFIG_TIME_CAPABILITY PHY_OFF(0xE0) -#define UFS_PHY_TX_MIN_STALL_NOCONFIG_TIME_CAPABILITY PHY_OFF(0xB8) -#define UFS_PHY_RX_MIN_STALL_NOCONFIG_TIME_CAPABILITY PHY_OFF(0xE4) -#define UFS_PHY_TX_MIN_SAVE_CONFIG_TIME_CAPABILITY PHY_OFF(0xBC) -#define UFS_PHY_RX_MIN_SAVE_CONFIG_TIME_CAPABILITY PHY_OFF(0xE8) -#define UFS_PHY_RX_PWM_BURST_CLOSURE_LENGTH_CAPABILITY PHY_OFF(0xFC) -#define UFS_PHY_RX_MIN_ACTIVATETIME_CAPABILITY PHY_OFF(0x100) -#define UFS_PHY_RX_SIGDET_CTRL3 PHY_OFF(0x14c) -#define UFS_PHY_RMMI_ATTR_CTRL PHY_OFF(0x160) -#define UFS_PHY_RMMI_RX_CFGUPDT_L1 (1 << 7) -#define UFS_PHY_RMMI_TX_CFGUPDT_L1 (1 << 6) -#define UFS_PHY_RMMI_CFGWR_L1 (1 << 5) -#define UFS_PHY_RMMI_CFGRD_L1 (1 << 4) -#define UFS_PHY_RMMI_RX_CFGUPDT_L0 (1 << 3) -#define UFS_PHY_RMMI_TX_CFGUPDT_L0 (1 << 2) -#define UFS_PHY_RMMI_CFGWR_L0 (1 << 1) -#define UFS_PHY_RMMI_CFGRD_L0 (1 << 0) -#define UFS_PHY_RMMI_ATTRID PHY_OFF(0x164) -#define UFS_PHY_RMMI_ATTRWRVAL PHY_OFF(0x168) -#define UFS_PHY_RMMI_ATTRRDVAL_L0_STATUS PHY_OFF(0x16C) -#define UFS_PHY_RMMI_ATTRRDVAL_L1_STATUS PHY_OFF(0x170) -#define UFS_PHY_PCS_READY_STATUS PHY_OFF(0x174) - -#define UFS_PHY_TX_LANE_ENABLE_MASK 0x3 - -/* - * This structure represents the 20nm specific phy. - * common_cfg MUST remain the first field in this structure - * in case extra fields are added. This way, when calling - * get_ufs_qcom_phy() of generic phy, we can extract the - * common phy structure (struct ufs_qcom_phy) out of it - * regardless of the relevant specific phy. - */ -struct ufs_qcom_phy_qmp_20nm { - struct ufs_qcom_phy common_cfg; -}; - -static struct ufs_qcom_phy_calibration phy_cal_table_rate_A_1_2_0[] = { - UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_POWER_DOWN_CONTROL, 0x01), - UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SIGDET_CTRL3, 0x0D), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_VCOTAIL_EN, 0xe1), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CRCTRL, 0xcc), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYSCLK_EN_SEL_TXBAND, 0x08), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CLKEPDIV, 0x03), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RXTXEPCLK_EN, 0x10), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START1, 0x82), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START2, 0x03), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START1, 0x80), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START2, 0x80), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START3, 0x40), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP1, 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP2, 0x19), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP3, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP_EN, 0x03), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL, 0x90), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL2, 0x03), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL1(0), 0xf2), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_HALF(0), 0x0c), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_QUARTER(0), 0x12), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL1(1), 0xf2), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_HALF(1), 0x0c), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_QUARTER(1), 0x12), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB(0), 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB(0), 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB(0), 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB(0), 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB(1), 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB(1), 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB(1), 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB(1), 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETI, 0x3f), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IP_SETP, 0x1b), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETP, 0x0f), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IP_SETI, 0x01), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_EMP_POST1_LVL(0), 0x2F), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_DRV_LVL(0), 0x20), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_EMP_POST1_LVL(1), 0x2F), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_DRV_LVL(1), 0x20), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE(0), 0x68), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE(1), 0x68), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2(1), 0xdc), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2(0), 0xdc), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x3), -}; - -static struct ufs_qcom_phy_calibration phy_cal_table_rate_A_1_3_0[] = { - UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_POWER_DOWN_CONTROL, 0x01), - UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SIGDET_CTRL3, 0x0D), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_VCOTAIL_EN, 0xe1), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CRCTRL, 0xcc), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYSCLK_EN_SEL_TXBAND, 0x08), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CLKEPDIV, 0x03), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RXTXEPCLK_EN, 0x10), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START1, 0x82), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START2, 0x03), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START1, 0x80), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START2, 0x80), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START3, 0x40), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP1, 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP2, 0x19), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP3, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP_EN, 0x03), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL, 0x90), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL2, 0x03), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL1(0), 0xf2), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_HALF(0), 0x0c), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_QUARTER(0), 0x12), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL1(1), 0xf2), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_HALF(1), 0x0c), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_QUARTER(1), 0x12), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB(0), 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB(0), 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB(0), 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB(0), 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB(1), 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB(1), 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB(1), 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB(1), 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETI, 0x2b), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IP_SETP, 0x38), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETP, 0x3c), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RES_CODE_UP_OFFSET, 0x02), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RES_CODE_DN_OFFSET, 0x02), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IP_SETI, 0x01), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CNTRL, 0x40), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE(0), 0x68), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE(1), 0x68), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2(1), 0xdc), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2(0), 0xdc), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x3), -}; - -static struct ufs_qcom_phy_calibration phy_cal_table_rate_B[] = { - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START1, 0x98), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP1, 0x65), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP2, 0x1e), -}; - -#endif diff --git a/drivers/phy/qualcomm/phy-qcom-ufs.c b/drivers/phy/qualcomm/phy-qcom-ufs.c deleted file mode 100644 index 763c8d396af1..000000000000 --- a/drivers/phy/qualcomm/phy-qcom-ufs.c +++ /dev/null @@ -1,648 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (c) 2013-2015, Linux Foundation. All rights reserved. - */ - -#include "phy-qcom-ufs-i.h" - -#define MAX_PROP_NAME 32 -#define VDDA_PHY_MIN_UV 1000000 -#define VDDA_PHY_MAX_UV 1000000 -#define VDDA_PLL_MIN_UV 1800000 -#define VDDA_PLL_MAX_UV 1800000 -#define VDDP_REF_CLK_MIN_UV 1200000 -#define VDDP_REF_CLK_MAX_UV 1200000 - -int ufs_qcom_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, - struct ufs_qcom_phy_calibration *tbl_A, - int tbl_size_A, - struct ufs_qcom_phy_calibration *tbl_B, - int tbl_size_B, bool is_rate_B) -{ - int i; - int ret = 0; - - if (!tbl_A) { - dev_err(ufs_qcom_phy->dev, "%s: tbl_A is NULL", __func__); - ret = EINVAL; - goto out; - } - - for (i = 0; i < tbl_size_A; i++) - writel_relaxed(tbl_A[i].cfg_value, - ufs_qcom_phy->mmio + tbl_A[i].reg_offset); - - /* - * In case we would like to work in rate B, we need - * to override a registers that were configured in rate A table - * with registers of rate B table. - * table. - */ - if (is_rate_B) { - if (!tbl_B) { - dev_err(ufs_qcom_phy->dev, "%s: tbl_B is NULL", - __func__); - ret = EINVAL; - goto out; - } - - for (i = 0; i < tbl_size_B; i++) - writel_relaxed(tbl_B[i].cfg_value, - ufs_qcom_phy->mmio + tbl_B[i].reg_offset); - } - - /* flush buffered writes */ - mb(); - -out: - return ret; -} -EXPORT_SYMBOL_GPL(ufs_qcom_phy_calibrate); - -/* - * This assumes the embedded phy structure inside generic_phy is of type - * struct ufs_qcom_phy. In order to function properly it's crucial - * to keep the embedded struct "struct ufs_qcom_phy common_cfg" - * as the first inside generic_phy. - */ -struct ufs_qcom_phy *get_ufs_qcom_phy(struct phy *generic_phy) -{ - return (struct ufs_qcom_phy *)phy_get_drvdata(generic_phy); -} -EXPORT_SYMBOL_GPL(get_ufs_qcom_phy); - -static -int ufs_qcom_phy_base_init(struct platform_device *pdev, - struct ufs_qcom_phy *phy_common) -{ - struct device *dev = &pdev->dev; - struct resource *res; - int err = 0; - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_mem"); - phy_common->mmio = devm_ioremap_resource(dev, res); - if (IS_ERR((void const *)phy_common->mmio)) { - err = PTR_ERR((void const *)phy_common->mmio); - phy_common->mmio = NULL; - dev_err(dev, "%s: ioremap for phy_mem resource failed %d\n", - __func__, err); - return err; - } - - /* "dev_ref_clk_ctrl_mem" is optional resource */ - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "dev_ref_clk_ctrl_mem"); - phy_common->dev_ref_clk_ctrl_mmio = devm_ioremap_resource(dev, res); - if (IS_ERR((void const *)phy_common->dev_ref_clk_ctrl_mmio)) - phy_common->dev_ref_clk_ctrl_mmio = NULL; - - return 0; -} - -struct phy *ufs_qcom_phy_generic_probe(struct platform_device *pdev, - struct ufs_qcom_phy *common_cfg, - const struct phy_ops *ufs_qcom_phy_gen_ops, - struct ufs_qcom_phy_specific_ops *phy_spec_ops) -{ - int err; - struct device *dev = &pdev->dev; - struct phy *generic_phy = NULL; - struct phy_provider *phy_provider; - - err = ufs_qcom_phy_base_init(pdev, common_cfg); - if (err) { - dev_err(dev, "%s: phy base init failed %d\n", __func__, err); - goto out; - } - - phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); - if (IS_ERR(phy_provider)) { - err = PTR_ERR(phy_provider); - dev_err(dev, "%s: failed to register phy %d\n", __func__, err); - goto out; - } - - generic_phy = devm_phy_create(dev, NULL, ufs_qcom_phy_gen_ops); - if (IS_ERR(generic_phy)) { - err = PTR_ERR(generic_phy); - dev_err(dev, "%s: failed to create phy %d\n", __func__, err); - generic_phy = NULL; - goto out; - } - - common_cfg->phy_spec_ops = phy_spec_ops; - common_cfg->dev = dev; - -out: - return generic_phy; -} -EXPORT_SYMBOL_GPL(ufs_qcom_phy_generic_probe); - -static int ufs_qcom_phy_get_reset(struct ufs_qcom_phy *phy_common) -{ - struct reset_control *reset; - - if (phy_common->ufs_reset) - return 0; - - reset = devm_reset_control_get_exclusive_by_index(phy_common->dev, 0); - if (IS_ERR(reset)) - return PTR_ERR(reset); - - phy_common->ufs_reset = reset; - return 0; -} - -static int __ufs_qcom_phy_clk_get(struct device *dev, - const char *name, struct clk **clk_out, bool err_print) -{ - struct clk *clk; - int err = 0; - - clk = devm_clk_get(dev, name); - if (IS_ERR(clk)) { - err = PTR_ERR(clk); - if (err_print) - dev_err(dev, "failed to get %s err %d", name, err); - } else { - *clk_out = clk; - } - - return err; -} - -static int ufs_qcom_phy_clk_get(struct device *dev, - const char *name, struct clk **clk_out) -{ - return __ufs_qcom_phy_clk_get(dev, name, clk_out, true); -} - -int ufs_qcom_phy_init_clks(struct ufs_qcom_phy *phy_common) -{ - int err; - - if (of_device_is_compatible(phy_common->dev->of_node, - "qcom,msm8996-ufs-phy-qmp-14nm")) - goto skip_txrx_clk; - - err = ufs_qcom_phy_clk_get(phy_common->dev, "tx_iface_clk", - &phy_common->tx_iface_clk); - if (err) - goto out; - - err = ufs_qcom_phy_clk_get(phy_common->dev, "rx_iface_clk", - &phy_common->rx_iface_clk); - if (err) - goto out; - -skip_txrx_clk: - err = ufs_qcom_phy_clk_get(phy_common->dev, "ref_clk_src", - &phy_common->ref_clk_src); - if (err) - goto out; - - /* - * "ref_clk_parent" is optional hence don't abort init if it's not - * found. - */ - __ufs_qcom_phy_clk_get(phy_common->dev, "ref_clk_parent", - &phy_common->ref_clk_parent, false); - - err = ufs_qcom_phy_clk_get(phy_common->dev, "ref_clk", - &phy_common->ref_clk); - -out: - return err; -} -EXPORT_SYMBOL_GPL(ufs_qcom_phy_init_clks); - -static int ufs_qcom_phy_init_vreg(struct device *dev, - struct ufs_qcom_phy_vreg *vreg, - const char *name) -{ - int err = 0; - - char prop_name[MAX_PROP_NAME]; - - vreg->name = name; - vreg->reg = devm_regulator_get(dev, name); - if (IS_ERR(vreg->reg)) { - err = PTR_ERR(vreg->reg); - dev_err(dev, "failed to get %s, %d\n", name, err); - goto out; - } - - if (dev->of_node) { - snprintf(prop_name, MAX_PROP_NAME, "%s-max-microamp", name); - err = of_property_read_u32(dev->of_node, - prop_name, &vreg->max_uA); - if (err && err != -EINVAL) { - dev_err(dev, "%s: failed to read %s\n", - __func__, prop_name); - goto out; - } else if (err == -EINVAL || !vreg->max_uA) { - if (regulator_count_voltages(vreg->reg) > 0) { - dev_err(dev, "%s: %s is mandatory\n", - __func__, prop_name); - goto out; - } - err = 0; - } - } - - if (!strcmp(name, "vdda-pll")) { - vreg->max_uV = VDDA_PLL_MAX_UV; - vreg->min_uV = VDDA_PLL_MIN_UV; - } else if (!strcmp(name, "vdda-phy")) { - vreg->max_uV = VDDA_PHY_MAX_UV; - vreg->min_uV = VDDA_PHY_MIN_UV; - } else if (!strcmp(name, "vddp-ref-clk")) { - vreg->max_uV = VDDP_REF_CLK_MAX_UV; - vreg->min_uV = VDDP_REF_CLK_MIN_UV; - } - -out: - return err; -} - -int ufs_qcom_phy_init_vregulators(struct ufs_qcom_phy *phy_common) -{ - int err; - - err = ufs_qcom_phy_init_vreg(phy_common->dev, &phy_common->vdda_pll, - "vdda-pll"); - if (err) - goto out; - - err = ufs_qcom_phy_init_vreg(phy_common->dev, &phy_common->vdda_phy, - "vdda-phy"); - - if (err) - goto out; - - err = ufs_qcom_phy_init_vreg(phy_common->dev, &phy_common->vddp_ref_clk, - "vddp-ref-clk"); - -out: - return err; -} -EXPORT_SYMBOL_GPL(ufs_qcom_phy_init_vregulators); - -static int ufs_qcom_phy_cfg_vreg(struct device *dev, - struct ufs_qcom_phy_vreg *vreg, bool on) -{ - int ret = 0; - struct regulator *reg = vreg->reg; - const char *name = vreg->name; - int min_uV; - int uA_load; - - if (regulator_count_voltages(reg) > 0) { - min_uV = on ? vreg->min_uV : 0; - ret = regulator_set_voltage(reg, min_uV, vreg->max_uV); - if (ret) { - dev_err(dev, "%s: %s set voltage failed, err=%d\n", - __func__, name, ret); - goto out; - } - uA_load = on ? vreg->max_uA : 0; - ret = regulator_set_load(reg, uA_load); - if (ret >= 0) { - /* - * regulator_set_load() returns new regulator - * mode upon success. - */ - ret = 0; - } else { - dev_err(dev, "%s: %s set optimum mode(uA_load=%d) failed, err=%d\n", - __func__, name, uA_load, ret); - goto out; - } - } -out: - return ret; -} - -static int ufs_qcom_phy_enable_vreg(struct device *dev, - struct ufs_qcom_phy_vreg *vreg) -{ - int ret = 0; - - if (!vreg || vreg->enabled) - goto out; - - ret = ufs_qcom_phy_cfg_vreg(dev, vreg, true); - if (ret) { - dev_err(dev, "%s: ufs_qcom_phy_cfg_vreg() failed, err=%d\n", - __func__, ret); - goto out; - } - - ret = regulator_enable(vreg->reg); - if (ret) { - dev_err(dev, "%s: enable failed, err=%d\n", - __func__, ret); - goto out; - } - - vreg->enabled = true; -out: - return ret; -} - -static int ufs_qcom_phy_enable_ref_clk(struct ufs_qcom_phy *phy) -{ - int ret = 0; - - if (phy->is_ref_clk_enabled) - goto out; - - /* - * reference clock is propagated in a daisy-chained manner from - * source to phy, so ungate them at each stage. - */ - ret = clk_prepare_enable(phy->ref_clk_src); - if (ret) { - dev_err(phy->dev, "%s: ref_clk_src enable failed %d\n", - __func__, ret); - goto out; - } - - /* - * "ref_clk_parent" is optional clock hence make sure that clk reference - * is available before trying to enable the clock. - */ - if (phy->ref_clk_parent) { - ret = clk_prepare_enable(phy->ref_clk_parent); - if (ret) { - dev_err(phy->dev, "%s: ref_clk_parent enable failed %d\n", - __func__, ret); - goto out_disable_src; - } - } - - ret = clk_prepare_enable(phy->ref_clk); - if (ret) { - dev_err(phy->dev, "%s: ref_clk enable failed %d\n", - __func__, ret); - goto out_disable_parent; - } - - phy->is_ref_clk_enabled = true; - goto out; - -out_disable_parent: - if (phy->ref_clk_parent) - clk_disable_unprepare(phy->ref_clk_parent); -out_disable_src: - clk_disable_unprepare(phy->ref_clk_src); -out: - return ret; -} - -static int ufs_qcom_phy_disable_vreg(struct device *dev, - struct ufs_qcom_phy_vreg *vreg) -{ - int ret = 0; - - if (!vreg || !vreg->enabled) - goto out; - - ret = regulator_disable(vreg->reg); - - if (!ret) { - /* ignore errors on applying disable config */ - ufs_qcom_phy_cfg_vreg(dev, vreg, false); - vreg->enabled = false; - } else { - dev_err(dev, "%s: %s disable failed, err=%d\n", - __func__, vreg->name, ret); - } -out: - return ret; -} - -static void ufs_qcom_phy_disable_ref_clk(struct ufs_qcom_phy *phy) -{ - if (phy->is_ref_clk_enabled) { - clk_disable_unprepare(phy->ref_clk); - /* - * "ref_clk_parent" is optional clock hence make sure that clk - * reference is available before trying to disable the clock. - */ - if (phy->ref_clk_parent) - clk_disable_unprepare(phy->ref_clk_parent); - clk_disable_unprepare(phy->ref_clk_src); - phy->is_ref_clk_enabled = false; - } -} - -/* Turn ON M-PHY RMMI interface clocks */ -static int ufs_qcom_phy_enable_iface_clk(struct ufs_qcom_phy *phy) -{ - int ret = 0; - - if (phy->is_iface_clk_enabled) - goto out; - - ret = clk_prepare_enable(phy->tx_iface_clk); - if (ret) { - dev_err(phy->dev, "%s: tx_iface_clk enable failed %d\n", - __func__, ret); - goto out; - } - ret = clk_prepare_enable(phy->rx_iface_clk); - if (ret) { - clk_disable_unprepare(phy->tx_iface_clk); - dev_err(phy->dev, "%s: rx_iface_clk enable failed %d. disabling also tx_iface_clk\n", - __func__, ret); - goto out; - } - phy->is_iface_clk_enabled = true; - -out: - return ret; -} - -/* Turn OFF M-PHY RMMI interface clocks */ -static void ufs_qcom_phy_disable_iface_clk(struct ufs_qcom_phy *phy) -{ - if (phy->is_iface_clk_enabled) { - clk_disable_unprepare(phy->tx_iface_clk); - clk_disable_unprepare(phy->rx_iface_clk); - phy->is_iface_clk_enabled = false; - } -} - -static int ufs_qcom_phy_start_serdes(struct ufs_qcom_phy *ufs_qcom_phy) -{ - int ret = 0; - - if (!ufs_qcom_phy->phy_spec_ops->start_serdes) { - dev_err(ufs_qcom_phy->dev, "%s: start_serdes() callback is not supported\n", - __func__); - ret = -ENOTSUPP; - } else { - ufs_qcom_phy->phy_spec_ops->start_serdes(ufs_qcom_phy); - } - - return ret; -} - -int ufs_qcom_phy_set_tx_lane_enable(struct phy *generic_phy, u32 tx_lanes) -{ - struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy); - int ret = 0; - - if (!ufs_qcom_phy->phy_spec_ops->set_tx_lane_enable) { - dev_err(ufs_qcom_phy->dev, "%s: set_tx_lane_enable() callback is not supported\n", - __func__); - ret = -ENOTSUPP; - } else { - ufs_qcom_phy->phy_spec_ops->set_tx_lane_enable(ufs_qcom_phy, - tx_lanes); - } - - return ret; -} -EXPORT_SYMBOL_GPL(ufs_qcom_phy_set_tx_lane_enable); - -void ufs_qcom_phy_save_controller_version(struct phy *generic_phy, - u8 major, u16 minor, u16 step) -{ - struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy); - - ufs_qcom_phy->host_ctrl_rev_major = major; - ufs_qcom_phy->host_ctrl_rev_minor = minor; - ufs_qcom_phy->host_ctrl_rev_step = step; -} -EXPORT_SYMBOL_GPL(ufs_qcom_phy_save_controller_version); - -static int ufs_qcom_phy_is_pcs_ready(struct ufs_qcom_phy *ufs_qcom_phy) -{ - if (!ufs_qcom_phy->phy_spec_ops->is_physical_coding_sublayer_ready) { - dev_err(ufs_qcom_phy->dev, "%s: is_physical_coding_sublayer_ready() callback is not supported\n", - __func__); - return -ENOTSUPP; - } - - return ufs_qcom_phy->phy_spec_ops-> - is_physical_coding_sublayer_ready(ufs_qcom_phy); -} - -int ufs_qcom_phy_power_on(struct phy *generic_phy) -{ - struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy); - struct device *dev = phy_common->dev; - bool is_rate_B = false; - int err; - - err = ufs_qcom_phy_get_reset(phy_common); - if (err) - return err; - - err = reset_control_assert(phy_common->ufs_reset); - if (err) - return err; - - if (phy_common->mode == PHY_MODE_UFS_HS_B) - is_rate_B = true; - - err = phy_common->phy_spec_ops->calibrate(phy_common, is_rate_B); - if (err) - return err; - - err = reset_control_deassert(phy_common->ufs_reset); - if (err) { - dev_err(dev, "Failed to assert UFS PHY reset"); - return err; - } - - err = ufs_qcom_phy_start_serdes(phy_common); - if (err) - return err; - - err = ufs_qcom_phy_is_pcs_ready(phy_common); - if (err) - return err; - - err = ufs_qcom_phy_enable_vreg(dev, &phy_common->vdda_phy); - if (err) { - dev_err(dev, "%s enable vdda_phy failed, err=%d\n", - __func__, err); - goto out; - } - - phy_common->phy_spec_ops->power_control(phy_common, true); - - /* vdda_pll also enables ref clock LDOs so enable it first */ - err = ufs_qcom_phy_enable_vreg(dev, &phy_common->vdda_pll); - if (err) { - dev_err(dev, "%s enable vdda_pll failed, err=%d\n", - __func__, err); - goto out_disable_phy; - } - - err = ufs_qcom_phy_enable_iface_clk(phy_common); - if (err) { - dev_err(dev, "%s enable phy iface clock failed, err=%d\n", - __func__, err); - goto out_disable_pll; - } - - err = ufs_qcom_phy_enable_ref_clk(phy_common); - if (err) { - dev_err(dev, "%s enable phy ref clock failed, err=%d\n", - __func__, err); - goto out_disable_iface_clk; - } - - /* enable device PHY ref_clk pad rail */ - if (phy_common->vddp_ref_clk.reg) { - err = ufs_qcom_phy_enable_vreg(dev, - &phy_common->vddp_ref_clk); - if (err) { - dev_err(dev, "%s enable vddp_ref_clk failed, err=%d\n", - __func__, err); - goto out_disable_ref_clk; - } - } - - goto out; - -out_disable_ref_clk: - ufs_qcom_phy_disable_ref_clk(phy_common); -out_disable_iface_clk: - ufs_qcom_phy_disable_iface_clk(phy_common); -out_disable_pll: - ufs_qcom_phy_disable_vreg(dev, &phy_common->vdda_pll); -out_disable_phy: - ufs_qcom_phy_disable_vreg(dev, &phy_common->vdda_phy); -out: - return err; -} -EXPORT_SYMBOL_GPL(ufs_qcom_phy_power_on); - -int ufs_qcom_phy_power_off(struct phy *generic_phy) -{ - struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy); - - phy_common->phy_spec_ops->power_control(phy_common, false); - - if (phy_common->vddp_ref_clk.reg) - ufs_qcom_phy_disable_vreg(phy_common->dev, - &phy_common->vddp_ref_clk); - ufs_qcom_phy_disable_ref_clk(phy_common); - ufs_qcom_phy_disable_iface_clk(phy_common); - - ufs_qcom_phy_disable_vreg(phy_common->dev, &phy_common->vdda_pll); - ufs_qcom_phy_disable_vreg(phy_common->dev, &phy_common->vdda_phy); - reset_control_assert(phy_common->ufs_reset); - return 0; -} -EXPORT_SYMBOL_GPL(ufs_qcom_phy_power_off); - -MODULE_AUTHOR("Yaniv Gardi "); -MODULE_AUTHOR("Vivek Gautam "); -MODULE_DESCRIPTION("Universal Flash Storage (UFS) QCOM PHY"); -MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From b555f35f2f87f8a99ba8e65d3f51ae4294748b58 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Mon, 29 Jun 2020 20:20:07 +0530 Subject: phy: core: fix code style in devm_of_phy_provider_unregister Documentation/process/coding-style.rst says: "functions: they have the opening brace at the beginning of the next line" devm_of_phy_provider_unregister() function has opening brace at same line, so fix it up. Link: https://lore.kernel.org/r/20200629145010.122675-1-vkoul@kernel.org Signed-off-by: Vinod Koul --- drivers/phy/phy-core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c index a27b8d578d7f..b54522f06245 100644 --- a/drivers/phy/phy-core.c +++ b/drivers/phy/phy-core.c @@ -1122,7 +1122,8 @@ EXPORT_SYMBOL_GPL(of_phy_provider_unregister); * of_phy_provider_unregister to unregister the phy provider. */ void devm_of_phy_provider_unregister(struct device *dev, - struct phy_provider *phy_provider) { + struct phy_provider *phy_provider) +{ int r; r = devres_destroy(dev, devm_phy_provider_release, devm_phy_match, -- cgit v1.2.3-59-g8ed1b From aad075c155bcc4265e62e1f6fecab7db186d9572 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Mon, 29 Jun 2020 20:20:08 +0530 Subject: phy: core: Document function args Some function arguments are missing from documentation prompting validation kernel doc script to complain: drivers/phy/phy-core.c:1078: warning: Function parameter or member 'children' not described in '__devm_of_phy_provider_register' drivers/phy/phy-core.c:1125: warning: Function parameter or member 'phy_provider' not described in 'devm_of_phy_provider_unregister' Add the documentation for these Link: https://lore.kernel.org/r/20200629145010.122675-2-vkoul@kernel.org Signed-off-by: Vinod Koul --- drivers/phy/phy-core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c index b54522f06245..71cb10826326 100644 --- a/drivers/phy/phy-core.c +++ b/drivers/phy/phy-core.c @@ -1062,6 +1062,7 @@ EXPORT_SYMBOL_GPL(__of_phy_provider_register); * __devm_of_phy_provider_register() - create/register phy provider with the * framework * @dev: struct device of the phy provider + * @children: device node containing children (if different from dev->of_node) * @owner: the module owner containing of_xlate * @of_xlate: function pointer to obtain phy instance from phy provider * @@ -1117,6 +1118,7 @@ EXPORT_SYMBOL_GPL(of_phy_provider_unregister); /** * devm_of_phy_provider_unregister() - remove phy provider from the framework * @dev: struct device of the phy provider + * @phy_provider: phy provider returned by of_phy_provider_register() * * destroys the devres associated with this phy provider and invokes * of_phy_provider_unregister to unregister the phy provider. -- cgit v1.2.3-59-g8ed1b From 3b0163bb34f15695feb0b87365bed3eba8f9804d Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Mon, 29 Jun 2020 20:20:09 +0530 Subject: phy: ti: dm816x: remove set but unused variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dm816x_usb_phy_init() sets variable 'error' but never uses it, so remove it. drivers/phy/ti/phy-dm816x-usb.c:85:6: warning: variable ‘error’ set but not used [-Wunused-but-set-variable] Link: https://lore.kernel.org/r/20200629145010.122675-3-vkoul@kernel.org Signed-off-by: Vinod Koul --- drivers/phy/ti/phy-dm816x-usb.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/phy/ti/phy-dm816x-usb.c b/drivers/phy/ti/phy-dm816x-usb.c index 26f194779064..57adc08a89b2 100644 --- a/drivers/phy/ti/phy-dm816x-usb.c +++ b/drivers/phy/ti/phy-dm816x-usb.c @@ -82,17 +82,16 @@ static int dm816x_usb_phy_init(struct phy *x) { struct dm816x_usb_phy *phy = phy_get_drvdata(x); unsigned int val; - int error; if (clk_get_rate(phy->refclk) != 24000000) dev_warn(phy->dev, "nonstandard phy refclk\n"); /* Set PLL ref clock and put phys to sleep */ - error = regmap_update_bits(phy->syscon, phy->usb_ctrl, - DM816X_USB_CTRL_PHYCLKSRC | - DM816X_USB_CTRL_PHYSLEEP1 | - DM816X_USB_CTRL_PHYSLEEP0, - 0); + regmap_update_bits(phy->syscon, phy->usb_ctrl, + DM816X_USB_CTRL_PHYCLKSRC | + DM816X_USB_CTRL_PHYSLEEP1 | + DM816X_USB_CTRL_PHYSLEEP0, + 0); regmap_read(phy->syscon, phy->usb_ctrl, &val); if ((val & 3) != 0) dev_info(phy->dev, -- cgit v1.2.3-59-g8ed1b From 3cc4502ce8f37b4f8a75bfc4eb6d99a74ce018e1 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Mon, 29 Jun 2020 20:20:10 +0530 Subject: phy: ti-pipe3: remove set but unused variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ti_pipe3_power_on() sets variable 'ret' but never uses it, so remove it. drivers/phy/ti/phy-ti-pipe3.c:340:6: warning: variable ‘ret’ set but not used [-Wunused-but-set-variable] Link: https://lore.kernel.org/r/20200629145010.122675-4-vkoul@kernel.org Signed-off-by: Vinod Koul --- drivers/phy/ti/phy-ti-pipe3.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/phy/ti/phy-ti-pipe3.c b/drivers/phy/ti/phy-ti-pipe3.c index a87946589eb7..e9332c90f75f 100644 --- a/drivers/phy/ti/phy-ti-pipe3.c +++ b/drivers/phy/ti/phy-ti-pipe3.c @@ -337,7 +337,6 @@ static int ti_pipe3_power_on(struct phy *x) { u32 val; u32 mask; - int ret; unsigned long rate; struct ti_pipe3 *phy = phy_get_drvdata(x); bool rx_pending = false; @@ -355,8 +354,8 @@ static int ti_pipe3_power_on(struct phy *x) rate = rate / 1000000; mask = OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_MASK; val = rate << OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_SHIFT; - ret = regmap_update_bits(phy->phy_power_syscon, phy->power_reg, - mask, val); + regmap_update_bits(phy->phy_power_syscon, phy->power_reg, + mask, val); /* * For PCIe, TX and RX must be powered on simultaneously. * For USB and SATA, TX must be powered on before RX -- cgit v1.2.3-59-g8ed1b From 82c8d38699009697f3136f5347d0903befdce810 Mon Sep 17 00:00:00 2001 From: Rikard Falkeborn Date: Mon, 29 Jun 2020 21:57:27 +0200 Subject: phy: allwinner: phy-sun6i-mipi-dphy: Constify structs sun6i_dphy_ops and sun6i_dphy_regmap_config are not modified so make them const structs to allow the compiler to put them in read-only memory. Before: text data bss dec hex filename 4407 1944 64 6415 190f drivers/phy/allwinner/phy-sun6i-mipi-dphy.o After: text data bss dec hex filename 4835 1496 64 6395 18fb drivers/phy/allwinner/phy-sun6i-mipi-dphy.o Signed-off-by: Rikard Falkeborn Acked-by: Maxime Ripard Link: https://lore.kernel.org/r/20200629195727.9717-1-rikard.falkeborn@gmail.com Signed-off-by: Vinod Koul --- drivers/phy/allwinner/phy-sun6i-mipi-dphy.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/phy/allwinner/phy-sun6i-mipi-dphy.c b/drivers/phy/allwinner/phy-sun6i-mipi-dphy.c index 79c8af5c7c1d..1fa761ba6cbb 100644 --- a/drivers/phy/allwinner/phy-sun6i-mipi-dphy.c +++ b/drivers/phy/allwinner/phy-sun6i-mipi-dphy.c @@ -233,7 +233,7 @@ static int sun6i_dphy_exit(struct phy *phy) } -static struct phy_ops sun6i_dphy_ops = { +static const struct phy_ops sun6i_dphy_ops = { .configure = sun6i_dphy_configure, .power_on = sun6i_dphy_power_on, .power_off = sun6i_dphy_power_off, @@ -241,7 +241,7 @@ static struct phy_ops sun6i_dphy_ops = { .exit = sun6i_dphy_exit, }; -static struct regmap_config sun6i_dphy_regmap_config = { +static const struct regmap_config sun6i_dphy_regmap_config = { .reg_bits = 32, .val_bits = 32, .reg_stride = 4, -- cgit v1.2.3-59-g8ed1b From fd01cc590357df1da6e3ec8c9947ee15a83a40ec Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 9 Jul 2020 14:08:53 +0200 Subject: virt: vbox: Rename guest_caps struct members to set_guest_caps Rename guest_caps[_tracker] struct members to set_guest_caps[_tracker] this is a preparation patch for adding support for the VBGL_IOCTL_GUEST_CAPS_ACQUIRE ioctl. Acked-by: Arnd Bergmann Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20200709120858.63928-4-hdegoede@redhat.com Signed-off-by: Greg Kroah-Hartman --- drivers/virt/vboxguest/vboxguest_core.c | 20 ++++++++++---------- drivers/virt/vboxguest/vboxguest_core.h | 9 +++++---- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/drivers/virt/vboxguest/vboxguest_core.c b/drivers/virt/vboxguest/vboxguest_core.c index 18ebd7a6af98..aee5eff229f2 100644 --- a/drivers/virt/vboxguest/vboxguest_core.c +++ b/drivers/virt/vboxguest/vboxguest_core.c @@ -699,17 +699,17 @@ static int vbg_set_session_capabilities(struct vbg_dev *gdev, mutex_lock(&gdev->session_mutex); /* Apply the changes to the session mask. */ - previous = session->guest_caps; - session->guest_caps |= or_mask; - session->guest_caps &= ~not_mask; + previous = session->set_guest_caps; + session->set_guest_caps |= or_mask; + session->set_guest_caps &= ~not_mask; /* If anything actually changed, update the global usage counters. */ - changed = previous ^ session->guest_caps; + changed = previous ^ session->set_guest_caps; if (!changed) goto out; - vbg_track_bit_usage(&gdev->guest_caps_tracker, changed, previous); - or_mask = gdev->guest_caps_tracker.mask; + vbg_track_bit_usage(&gdev->set_guest_caps_tracker, changed, previous); + or_mask = gdev->set_guest_caps_tracker.mask; if (gdev->guest_caps_host == or_mask || !req) goto out; @@ -726,9 +726,9 @@ static int vbg_set_session_capabilities(struct vbg_dev *gdev, if (session_termination) goto out; - vbg_track_bit_usage(&gdev->guest_caps_tracker, changed, - session->guest_caps); - session->guest_caps = previous; + vbg_track_bit_usage(&gdev->set_guest_caps_tracker, changed, + session->set_guest_caps); + session->set_guest_caps = previous; } out: @@ -1452,7 +1452,7 @@ static int vbg_ioctl_change_guest_capabilities(struct vbg_dev *gdev, if (ret) return ret; - caps->u.out.session_caps = session->guest_caps; + caps->u.out.session_caps = session->set_guest_caps; caps->u.out.global_caps = gdev->guest_caps_host; return 0; diff --git a/drivers/virt/vboxguest/vboxguest_core.h b/drivers/virt/vboxguest/vboxguest_core.h index 77c3a9c8255d..dc745a033164 100644 --- a/drivers/virt/vboxguest/vboxguest_core.h +++ b/drivers/virt/vboxguest/vboxguest_core.h @@ -118,11 +118,12 @@ struct vbg_dev { u32 event_filter_host; /** - * Usage counters for guest capabilities. Indexed by capability bit + * Usage counters for guest capabilities requested through + * vbg_set_session_capabilities(). Indexed by capability bit * number, one count per session using a capability. * Protected by session_mutex. */ - struct vbg_bit_usage_tracker guest_caps_tracker; + struct vbg_bit_usage_tracker set_guest_caps_tracker; /** * The guest capabilities last reported to the host (or UINT32_MAX). * Protected by session_mutex. @@ -164,11 +165,11 @@ struct vbg_session { */ u32 event_filter; /** - * Guest capabilities for this session. + * Guest capabilities set through vbg_set_session_capabilities(). * A capability claimed by any guest session will be reported to the * host. Protected by vbg_gdev.session_mutex. */ - u32 guest_caps; + u32 set_guest_caps; /** VMMDEV_REQUESTOR_* flags */ u32 requestor; /** Set on CANCEL_ALL_WAITEVENTS, protected by vbg_devevent_spinlock. */ -- cgit v1.2.3-59-g8ed1b From 412f84cff0c9aa95fd204961f21121f38d77cc13 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 9 Jul 2020 14:08:54 +0200 Subject: virt: vbox: Add vbg_set_host_capabilities() helper function Add vbg_set_host_capabilities() helper function, this is a preparation patch for adding support for the VBGL_IOCTL_GUEST_CAPS_ACQUIRE ioctl. Acked-by: Arnd Bergmann Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20200709120858.63928-5-hdegoede@redhat.com Signed-off-by: Greg Kroah-Hartman --- drivers/virt/vboxguest/vboxguest_core.c | 79 +++++++++++++++++++-------------- 1 file changed, 46 insertions(+), 33 deletions(-) diff --git a/drivers/virt/vboxguest/vboxguest_core.c b/drivers/virt/vboxguest/vboxguest_core.c index aee5eff229f2..15b3cb618c6e 100644 --- a/drivers/virt/vboxguest/vboxguest_core.c +++ b/drivers/virt/vboxguest/vboxguest_core.c @@ -661,6 +661,48 @@ static int vbg_reset_host_capabilities(struct vbg_dev *gdev) return vbg_status_code_to_errno(rc); } +/** + * Set guest capabilities on the host. + * Must be called with gdev->session_mutex hold. + * Return: 0 or negative errno value. + * @gdev: The Guest extension device. + * @session: The session. + * @session_termination: Set if we're called by the session cleanup code. + */ +static int vbg_set_host_capabilities(struct vbg_dev *gdev, + struct vbg_session *session, + bool session_termination) +{ + struct vmmdev_mask *req; + u32 caps; + int rc; + + WARN_ON(!mutex_is_locked(&gdev->session_mutex)); + + caps = gdev->set_guest_caps_tracker.mask; + + if (gdev->guest_caps_host == caps) + return 0; + + /* On termination the requestor is the kernel, as we're cleaning up. */ + req = vbg_req_alloc(sizeof(*req), VMMDEVREQ_SET_GUEST_CAPABILITIES, + session_termination ? VBG_KERNEL_REQUEST : + session->requestor); + if (!req) { + gdev->guest_caps_host = U32_MAX; + return -ENOMEM; + } + + req->or_mask = caps; + req->not_mask = ~caps; + rc = vbg_req_perform(gdev, req); + vbg_req_free(req, sizeof(*req)); + + gdev->guest_caps_host = (rc >= 0) ? caps : U32_MAX; + + return vbg_status_code_to_errno(rc); +} + /** * Sets the guest capabilities for a session. Takes the session spinlock. * Return: 0 or negative errno value. @@ -678,23 +720,8 @@ static int vbg_set_session_capabilities(struct vbg_dev *gdev, u32 or_mask, u32 not_mask, bool session_termination) { - struct vmmdev_mask *req; u32 changed, previous; - int rc, ret = 0; - - /* - * Allocate a request buffer before taking the spinlock, when - * the session is being terminated the requestor is the kernel, - * as we're cleaning up. - */ - req = vbg_req_alloc(sizeof(*req), VMMDEVREQ_SET_GUEST_CAPABILITIES, - session_termination ? VBG_KERNEL_REQUEST : - session->requestor); - if (!req) { - if (!session_termination) - return -ENOMEM; - /* Ignore allocation failure, we must do session cleanup. */ - } + int ret = 0; mutex_lock(&gdev->session_mutex); @@ -709,23 +736,10 @@ static int vbg_set_session_capabilities(struct vbg_dev *gdev, goto out; vbg_track_bit_usage(&gdev->set_guest_caps_tracker, changed, previous); - or_mask = gdev->set_guest_caps_tracker.mask; - - if (gdev->guest_caps_host == or_mask || !req) - goto out; - - gdev->guest_caps_host = or_mask; - req->or_mask = or_mask; - req->not_mask = ~or_mask; - rc = vbg_req_perform(gdev, req); - if (rc < 0) { - ret = vbg_status_code_to_errno(rc); - - /* Failed, roll back (unless it's session termination time). */ - gdev->guest_caps_host = U32_MAX; - if (session_termination) - goto out; + ret = vbg_set_host_capabilities(gdev, session, session_termination); + /* Roll back on failure, unless it's session termination time. */ + if (ret < 0 && !session_termination) { vbg_track_bit_usage(&gdev->set_guest_caps_tracker, changed, session->set_guest_caps); session->set_guest_caps = previous; @@ -733,7 +747,6 @@ static int vbg_set_session_capabilities(struct vbg_dev *gdev, out: mutex_unlock(&gdev->session_mutex); - vbg_req_free(req, sizeof(*req)); return ret; } -- cgit v1.2.3-59-g8ed1b From 631beddc5466731b048263a4a9d3d67150e72f8d Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 9 Jul 2020 14:08:55 +0200 Subject: virt: vbox: Add support for the new VBG_IOCTL_ACQUIRE_GUEST_CAPABILITIES ioctl Add support for the new VBG_IOCTL_ACQUIRE_GUEST_CAPABILITIES ioctl, this is necessary for automatic resizing of the guest resolution to match the VM-window size to work with the new VMSVGA virtual GPU which is now the new default in VirtualBox. BugLink: https://bugzilla.redhat.com/show_bug.cgi?id=1789545 Acked-by: Arnd Bergmann Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20200709120858.63928-6-hdegoede@redhat.com Signed-off-by: Greg Kroah-Hartman --- drivers/virt/vboxguest/vboxguest_core.c | 163 +++++++++++++++++++++++++++++++- drivers/virt/vboxguest/vboxguest_core.h | 14 +++ include/uapi/linux/vboxguest.h | 24 +++++ 3 files changed, 200 insertions(+), 1 deletion(-) diff --git a/drivers/virt/vboxguest/vboxguest_core.c b/drivers/virt/vboxguest/vboxguest_core.c index 15b3cb618c6e..4f1addaa3f6f 100644 --- a/drivers/virt/vboxguest/vboxguest_core.c +++ b/drivers/virt/vboxguest/vboxguest_core.c @@ -679,7 +679,7 @@ static int vbg_set_host_capabilities(struct vbg_dev *gdev, WARN_ON(!mutex_is_locked(&gdev->session_mutex)); - caps = gdev->set_guest_caps_tracker.mask; + caps = gdev->acquired_guest_caps | gdev->set_guest_caps_tracker.mask; if (gdev->guest_caps_host == caps) return 0; @@ -703,6 +703,113 @@ static int vbg_set_host_capabilities(struct vbg_dev *gdev, return vbg_status_code_to_errno(rc); } +/** + * Acquire (get exclusive access) guest capabilities for a session. + * Takes the session mutex. + * Return: 0 or negative errno value. + * @gdev: The Guest extension device. + * @session: The session. + * @flags: Flags (VBGL_IOC_AGC_FLAGS_XXX). + * @or_mask: The capabilities to add. + * @not_mask: The capabilities to remove. + * @session_termination: Set if we're called by the session cleanup code. + * This tweaks the error handling so we perform + * proper session cleanup even if the host + * misbehaves. + */ +static int vbg_acquire_session_capabilities(struct vbg_dev *gdev, + struct vbg_session *session, + u32 or_mask, u32 not_mask, + u32 flags, bool session_termination) +{ + unsigned long irqflags; + bool wakeup = false; + int ret = 0; + + mutex_lock(&gdev->session_mutex); + + if (gdev->set_guest_caps_tracker.mask & or_mask) { + vbg_err("%s error: cannot acquire caps which are currently set\n", + __func__); + ret = -EINVAL; + goto out; + } + + /* + * Mark any caps in the or_mask as now being in acquire-mode. Note + * once caps are in acquire_mode they always stay in this mode. + * This impacts event handling, so we take the event-lock. + */ + spin_lock_irqsave(&gdev->event_spinlock, irqflags); + gdev->acquire_mode_guest_caps |= or_mask; + spin_unlock_irqrestore(&gdev->event_spinlock, irqflags); + + /* If we only have to switch the caps to acquire mode, we're done. */ + if (flags & VBGL_IOC_AGC_FLAGS_CONFIG_ACQUIRE_MODE) + goto out; + + not_mask &= ~or_mask; /* or_mask takes priority over not_mask */ + not_mask &= session->acquired_guest_caps; + or_mask &= ~session->acquired_guest_caps; + + if (or_mask == 0 && not_mask == 0) + goto out; + + if (gdev->acquired_guest_caps & or_mask) { + ret = -EBUSY; + goto out; + } + + gdev->acquired_guest_caps |= or_mask; + gdev->acquired_guest_caps &= ~not_mask; + /* session->acquired_guest_caps impacts event handling, take the lock */ + spin_lock_irqsave(&gdev->event_spinlock, irqflags); + session->acquired_guest_caps |= or_mask; + session->acquired_guest_caps &= ~not_mask; + spin_unlock_irqrestore(&gdev->event_spinlock, irqflags); + + ret = vbg_set_host_capabilities(gdev, session, session_termination); + /* Roll back on failure, unless it's session termination time. */ + if (ret < 0 && !session_termination) { + gdev->acquired_guest_caps &= ~or_mask; + gdev->acquired_guest_caps |= not_mask; + spin_lock_irqsave(&gdev->event_spinlock, irqflags); + session->acquired_guest_caps &= ~or_mask; + session->acquired_guest_caps |= not_mask; + spin_unlock_irqrestore(&gdev->event_spinlock, irqflags); + } + + /* + * If we added a capability, check if that means some other thread in + * our session should be unblocked because there are events pending + * (the result of vbg_get_allowed_event_mask_for_session() may change). + * + * HACK ALERT! When the seamless support capability is added we generate + * a seamless change event so that the ring-3 client can sync with + * the seamless state. + */ + if (ret == 0 && or_mask != 0) { + spin_lock_irqsave(&gdev->event_spinlock, irqflags); + + if (or_mask & VMMDEV_GUEST_SUPPORTS_SEAMLESS) + gdev->pending_events |= + VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST; + + if (gdev->pending_events) + wakeup = true; + + spin_unlock_irqrestore(&gdev->event_spinlock, irqflags); + + if (wakeup) + wake_up(&gdev->event_wq); + } + +out: + mutex_unlock(&gdev->session_mutex); + + return ret; +} + /** * Sets the guest capabilities for a session. Takes the session spinlock. * Return: 0 or negative errno value. @@ -725,6 +832,13 @@ static int vbg_set_session_capabilities(struct vbg_dev *gdev, mutex_lock(&gdev->session_mutex); + if (gdev->acquire_mode_guest_caps & or_mask) { + vbg_err("%s error: cannot set caps which are in acquire_mode\n", + __func__); + ret = -EBUSY; + goto out; + } + /* Apply the changes to the session mask. */ previous = session->set_guest_caps; session->set_guest_caps |= or_mask; @@ -962,6 +1076,7 @@ void vbg_core_close_session(struct vbg_session *session) struct vbg_dev *gdev = session->gdev; int i, rc; + vbg_acquire_session_capabilities(gdev, session, 0, U32_MAX, 0, true); vbg_set_session_capabilities(gdev, session, 0, U32_MAX, true); vbg_set_session_event_filter(gdev, session, 0, U32_MAX, true); @@ -1019,6 +1134,25 @@ static int vbg_ioctl_driver_version_info( return 0; } +/* Must be called with the event_lock held */ +static u32 vbg_get_allowed_event_mask_for_session(struct vbg_dev *gdev, + struct vbg_session *session) +{ + u32 acquire_mode_caps = gdev->acquire_mode_guest_caps; + u32 session_acquired_caps = session->acquired_guest_caps; + u32 allowed_events = VMMDEV_EVENT_VALID_EVENT_MASK; + + if ((acquire_mode_caps & VMMDEV_GUEST_SUPPORTS_GRAPHICS) && + !(session_acquired_caps & VMMDEV_GUEST_SUPPORTS_GRAPHICS)) + allowed_events &= ~VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST; + + if ((acquire_mode_caps & VMMDEV_GUEST_SUPPORTS_SEAMLESS) && + !(session_acquired_caps & VMMDEV_GUEST_SUPPORTS_SEAMLESS)) + allowed_events &= ~VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST; + + return allowed_events; +} + static bool vbg_wait_event_cond(struct vbg_dev *gdev, struct vbg_session *session, u32 event_mask) @@ -1030,6 +1164,7 @@ static bool vbg_wait_event_cond(struct vbg_dev *gdev, spin_lock_irqsave(&gdev->event_spinlock, flags); events = gdev->pending_events & event_mask; + events &= vbg_get_allowed_event_mask_for_session(gdev, session); wakeup = events || session->cancel_waiters; spin_unlock_irqrestore(&gdev->event_spinlock, flags); @@ -1044,6 +1179,7 @@ static u32 vbg_consume_events_locked(struct vbg_dev *gdev, { u32 events = gdev->pending_events & event_mask; + events &= vbg_get_allowed_event_mask_for_session(gdev, session); gdev->pending_events &= ~events; return events; } @@ -1445,6 +1581,29 @@ static int vbg_ioctl_change_filter_mask(struct vbg_dev *gdev, false); } +static int vbg_ioctl_acquire_guest_capabilities(struct vbg_dev *gdev, + struct vbg_session *session, + struct vbg_ioctl_acquire_guest_caps *caps) +{ + u32 flags, or_mask, not_mask; + + if (vbg_ioctl_chk(&caps->hdr, sizeof(caps->u.in), 0)) + return -EINVAL; + + flags = caps->u.in.flags; + or_mask = caps->u.in.or_mask; + not_mask = caps->u.in.not_mask; + + if (flags & ~VBGL_IOC_AGC_FLAGS_VALID_MASK) + return -EINVAL; + + if ((or_mask | not_mask) & ~VMMDEV_GUEST_CAPABILITIES_MASK) + return -EINVAL; + + return vbg_acquire_session_capabilities(gdev, session, or_mask, + not_mask, flags, false); +} + static int vbg_ioctl_change_guest_capabilities(struct vbg_dev *gdev, struct vbg_session *session, struct vbg_ioctl_set_guest_caps *caps) { @@ -1554,6 +1713,8 @@ int vbg_core_ioctl(struct vbg_session *session, unsigned int req, void *data) return vbg_ioctl_interrupt_all_wait_events(gdev, session, data); case VBG_IOCTL_CHANGE_FILTER_MASK: return vbg_ioctl_change_filter_mask(gdev, session, data); + case VBG_IOCTL_ACQUIRE_GUEST_CAPABILITIES: + return vbg_ioctl_acquire_guest_capabilities(gdev, session, data); case VBG_IOCTL_CHANGE_GUEST_CAPABILITIES: return vbg_ioctl_change_guest_capabilities(gdev, session, data); case VBG_IOCTL_CHECK_BALLOON: diff --git a/drivers/virt/vboxguest/vboxguest_core.h b/drivers/virt/vboxguest/vboxguest_core.h index dc745a033164..ab4bf64e2cec 100644 --- a/drivers/virt/vboxguest/vboxguest_core.h +++ b/drivers/virt/vboxguest/vboxguest_core.h @@ -117,6 +117,15 @@ struct vbg_dev { */ u32 event_filter_host; + /** + * Guest capabilities which have been switched to acquire_mode. + */ + u32 acquire_mode_guest_caps; + /** + * Guest capabilities acquired by vbg_acquire_session_capabilities(). + * Only one session can acquire a capability at a time. + */ + u32 acquired_guest_caps; /** * Usage counters for guest capabilities requested through * vbg_set_session_capabilities(). Indexed by capability bit @@ -164,6 +173,11 @@ struct vbg_session { * host filter. Protected by vbg_gdev.session_mutex. */ u32 event_filter; + /** + * Guest capabilities acquired by vbg_acquire_session_capabilities(). + * Only one session can acquire a capability at a time. + */ + u32 acquired_guest_caps; /** * Guest capabilities set through vbg_set_session_capabilities(). * A capability claimed by any guest session will be reported to the diff --git a/include/uapi/linux/vboxguest.h b/include/uapi/linux/vboxguest.h index f79d7abe27db..15125f6ec60d 100644 --- a/include/uapi/linux/vboxguest.h +++ b/include/uapi/linux/vboxguest.h @@ -257,6 +257,30 @@ VMMDEV_ASSERT_SIZE(vbg_ioctl_change_filter, 24 + 8); _IOWR('V', 12, struct vbg_ioctl_change_filter) +/** VBG_IOCTL_ACQUIRE_GUEST_CAPABILITIES data structure. */ +struct vbg_ioctl_acquire_guest_caps { + /** The header. */ + struct vbg_ioctl_hdr hdr; + union { + struct { + /** Flags (VBGL_IOC_AGC_FLAGS_XXX). */ + __u32 flags; + /** Capabilities to set (VMMDEV_GUEST_SUPPORTS_XXX). */ + __u32 or_mask; + /** Capabilities to drop (VMMDEV_GUEST_SUPPORTS_XXX). */ + __u32 not_mask; + } in; + } u; +}; +VMMDEV_ASSERT_SIZE(vbg_ioctl_acquire_guest_caps, 24 + 12); + +#define VBGL_IOC_AGC_FLAGS_CONFIG_ACQUIRE_MODE 0x00000001 +#define VBGL_IOC_AGC_FLAGS_VALID_MASK 0x00000001 + +#define VBG_IOCTL_ACQUIRE_GUEST_CAPABILITIES \ + _IOWR('V', 13, struct vbg_ioctl_acquire_guest_caps) + + /** VBG_IOCTL_CHANGE_GUEST_CAPABILITIES data structure. */ struct vbg_ioctl_set_guest_caps { /** The header. */ -- cgit v1.2.3-59-g8ed1b From 316b0035402f05fe9e9e5334d1ff65dae285cb7c Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 9 Jul 2020 14:08:56 +0200 Subject: virt: vbox: Add a few new vmmdev request types to the userspace whitelist Upstream VirtualBox has defined and is using a few new request types for vmmdev requests passed through /dev/vboxguest to the hypervisor. Add the defines for these to vbox_vmmdev_types.h and add add them to the whitelists of vmmdev requests which userspace is allowed to make. BugLink: https://bugzilla.redhat.com/show_bug.cgi?id=1789545 Acked-by: Arnd Bergmann Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20200709120858.63928-7-hdegoede@redhat.com Signed-off-by: Greg Kroah-Hartman --- drivers/virt/vboxguest/vboxguest_core.c | 2 ++ include/uapi/linux/vbox_vmmdev_types.h | 3 +++ 2 files changed, 5 insertions(+) diff --git a/drivers/virt/vboxguest/vboxguest_core.c b/drivers/virt/vboxguest/vboxguest_core.c index 4f1addaa3f6f..ffd76b949276 100644 --- a/drivers/virt/vboxguest/vboxguest_core.c +++ b/drivers/virt/vboxguest/vboxguest_core.c @@ -1299,7 +1299,9 @@ static int vbg_req_allowed(struct vbg_dev *gdev, struct vbg_session *session, case VMMDEVREQ_VIDEO_ACCEL_ENABLE: case VMMDEVREQ_VIDEO_ACCEL_FLUSH: case VMMDEVREQ_VIDEO_SET_VISIBLE_REGION: + case VMMDEVREQ_VIDEO_UPDATE_MONITOR_POSITIONS: case VMMDEVREQ_GET_DISPLAY_CHANGE_REQEX: + case VMMDEVREQ_GET_DISPLAY_CHANGE_REQ_MULTI: case VMMDEVREQ_GET_SEAMLESS_CHANGE_REQ: case VMMDEVREQ_GET_VRDPCHANGE_REQ: case VMMDEVREQ_LOG_STRING: diff --git a/include/uapi/linux/vbox_vmmdev_types.h b/include/uapi/linux/vbox_vmmdev_types.h index c27289fd619a..f8a8d6b3c521 100644 --- a/include/uapi/linux/vbox_vmmdev_types.h +++ b/include/uapi/linux/vbox_vmmdev_types.h @@ -63,6 +63,7 @@ enum vmmdev_request_type { VMMDEVREQ_SET_GUEST_CAPABILITIES = 56, VMMDEVREQ_VIDEMODE_SUPPORTED2 = 57, /* since version 3.2.0 */ VMMDEVREQ_GET_DISPLAY_CHANGE_REQEX = 80, /* since version 4.2.4 */ + VMMDEVREQ_GET_DISPLAY_CHANGE_REQ_MULTI = 81, VMMDEVREQ_HGCM_CONNECT = 60, VMMDEVREQ_HGCM_DISCONNECT = 61, VMMDEVREQ_HGCM_CALL32 = 62, @@ -92,6 +93,8 @@ enum vmmdev_request_type { VMMDEVREQ_WRITE_COREDUMP = 218, VMMDEVREQ_GUEST_HEARTBEAT = 219, VMMDEVREQ_HEARTBEAT_CONFIGURE = 220, + VMMDEVREQ_NT_BUG_CHECK = 221, + VMMDEVREQ_VIDEO_UPDATE_MONITOR_POSITIONS = 222, /* Ensure the enum is a 32 bit data-type */ VMMDEVREQ_SIZEHACK = 0x7fffffff }; -- cgit v1.2.3-59-g8ed1b From 5bc117a27fd044bd5ddeb8ab22c58976bf01b50d Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 9 Jul 2020 14:08:57 +0200 Subject: virt: vbox: Log unknown ioctl requests as error Every now and then upstream adds new ioctls without notifying us, log unknown ioctl requests as an error to catch these. Acked-by: Arnd Bergmann Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20200709120858.63928-8-hdegoede@redhat.com Signed-off-by: Greg Kroah-Hartman --- drivers/virt/vboxguest/vboxguest_core.c | 2 +- drivers/virt/vboxguest/vboxguest_utils.c | 1 + include/linux/vbox_utils.h | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/virt/vboxguest/vboxguest_core.c b/drivers/virt/vboxguest/vboxguest_core.c index ffd76b949276..c5dfcd42fd07 100644 --- a/drivers/virt/vboxguest/vboxguest_core.c +++ b/drivers/virt/vboxguest/vboxguest_core.c @@ -1739,7 +1739,7 @@ int vbg_core_ioctl(struct vbg_session *session, unsigned int req, void *data) return vbg_ioctl_log(data); } - vbg_debug("VGDrvCommonIoCtl: Unknown req %#08x\n", req); + vbg_err_ratelimited("Userspace made an unknown ioctl req %#08x\n", req); return -ENOTTY; } diff --git a/drivers/virt/vboxguest/vboxguest_utils.c b/drivers/virt/vboxguest/vboxguest_utils.c index 7396187ee32a..ea05af41ec69 100644 --- a/drivers/virt/vboxguest/vboxguest_utils.c +++ b/drivers/virt/vboxguest/vboxguest_utils.c @@ -59,6 +59,7 @@ EXPORT_SYMBOL(name) VBG_LOG(vbg_info, pr_info); VBG_LOG(vbg_warn, pr_warn); VBG_LOG(vbg_err, pr_err); +VBG_LOG(vbg_err_ratelimited, pr_err_ratelimited); #if defined(DEBUG) && !defined(CONFIG_DYNAMIC_DEBUG) VBG_LOG(vbg_debug, pr_debug); #endif diff --git a/include/linux/vbox_utils.h b/include/linux/vbox_utils.h index ff56c443180c..db8a7d118093 100644 --- a/include/linux/vbox_utils.h +++ b/include/linux/vbox_utils.h @@ -16,6 +16,7 @@ struct vbg_dev; __printf(1, 2) void vbg_info(const char *fmt, ...); __printf(1, 2) void vbg_warn(const char *fmt, ...); __printf(1, 2) void vbg_err(const char *fmt, ...); +__printf(1, 2) void vbg_err_ratelimited(const char *fmt, ...); /* Only use backdoor logging for non-dynamic debug builds */ #if defined(DEBUG) && !defined(CONFIG_DYNAMIC_DEBUG) -- cgit v1.2.3-59-g8ed1b From 729082ed9b9cc242f5030de8f956b4be41150ea7 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 9 Jul 2020 14:08:58 +0200 Subject: virt: vbox: Fix some comments which talk about the "session spinlock" The session lock is a mutex, not a spinlock, fix the comments to match. Acked-by: Arnd Bergmann Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20200709120858.63928-9-hdegoede@redhat.com Signed-off-by: Greg Kroah-Hartman --- drivers/virt/vboxguest/vboxguest_core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/virt/vboxguest/vboxguest_core.c b/drivers/virt/vboxguest/vboxguest_core.c index c5dfcd42fd07..0b43efddea22 100644 --- a/drivers/virt/vboxguest/vboxguest_core.c +++ b/drivers/virt/vboxguest/vboxguest_core.c @@ -559,7 +559,7 @@ static int vbg_reset_host_event_filter(struct vbg_dev *gdev, * Changes the event filter mask for the given session. * * This is called in response to VBG_IOCTL_CHANGE_FILTER_MASK as well as to - * do session cleanup. Takes the session spinlock. + * do session cleanup. Takes the session mutex. * * Return: 0 or negative errno value. * @gdev: The Guest extension device. @@ -811,7 +811,7 @@ out: } /** - * Sets the guest capabilities for a session. Takes the session spinlock. + * Sets the guest capabilities for a session. Takes the session mutex. * Return: 0 or negative errno value. * @gdev: The Guest extension device. * @session: The session. -- cgit v1.2.3-59-g8ed1b From 8b66c917c41b966cd230d30c0268795e3f9eb5e4 Mon Sep 17 00:00:00 2001 From: Rikard Falkeborn Date: Wed, 1 Jul 2020 22:09:50 +0200 Subject: virtio_console: Constify some static variables The id_table and feature_table pointers in struct virtio_driver are pointers to const. Mark the corresponding static variables const to allow the compiler to put them in read-only memory. Before: text data bss dec hex filename 25447 713 76 26236 667c drivers/char/virtio_console.o After: text data bss dec hex filename 25488 673 76 26237 667d drivers/char/virtio_console.o Signed-off-by: Rikard Falkeborn Acked-by: Arnd Bergmann Reviewed-by: Amit Shah Link: https://lore.kernel.org/r/20200701200950.30314-6-rikard.falkeborn@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/char/virtio_console.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 00c5e3acee46..896f0ba9ba3c 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -2112,24 +2112,24 @@ fail: return err; } -static struct virtio_device_id id_table[] = { +static const struct virtio_device_id id_table[] = { { VIRTIO_ID_CONSOLE, VIRTIO_DEV_ANY_ID }, { 0 }, }; -static unsigned int features[] = { +static const unsigned int features[] = { VIRTIO_CONSOLE_F_SIZE, VIRTIO_CONSOLE_F_MULTIPORT, }; -static struct virtio_device_id rproc_serial_id_table[] = { +static const struct virtio_device_id rproc_serial_id_table[] = { #if IS_ENABLED(CONFIG_REMOTEPROC) { VIRTIO_ID_RPROC_SERIAL, VIRTIO_DEV_ANY_ID }, #endif { 0 }, }; -static unsigned int rproc_serial_features[] = { +static const unsigned int rproc_serial_features[] = { }; #ifdef CONFIG_PM_SLEEP -- cgit v1.2.3-59-g8ed1b From 51d501e540e6edd6737c0f1383b7789232595c13 Mon Sep 17 00:00:00 2001 From: Zhangfei Gao Date: Mon, 15 Jun 2020 21:55:57 +0800 Subject: uacce: remove uacce_vma_fault Fix NULL pointer error if removing uacce's parent module during app's running. SIGBUS is already reported by do_page_fault, so uacce_vma_fault is not needed. If providing vma_fault, vmf->page has to be filled as well, required by __do_fault. Reported-by: Jean-Philippe Brucker Signed-off-by: Zhangfei Gao Reviewed-by: Jean-Philippe Brucker Link: https://lore.kernel.org/r/1592229357-1904-1-git-send-email-zhangfei.gao@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/uacce/uacce.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/drivers/misc/uacce/uacce.c b/drivers/misc/uacce/uacce.c index 107028e77ca3..aa91f69a5fa9 100644 --- a/drivers/misc/uacce/uacce.c +++ b/drivers/misc/uacce/uacce.c @@ -179,14 +179,6 @@ static int uacce_fops_release(struct inode *inode, struct file *filep) return 0; } -static vm_fault_t uacce_vma_fault(struct vm_fault *vmf) -{ - if (vmf->flags & (FAULT_FLAG_MKWRITE | FAULT_FLAG_WRITE)) - return VM_FAULT_SIGBUS; - - return 0; -} - static void uacce_vma_close(struct vm_area_struct *vma) { struct uacce_queue *q = vma->vm_private_data; @@ -199,7 +191,6 @@ static void uacce_vma_close(struct vm_area_struct *vma) } static const struct vm_operations_struct uacce_vm_ops = { - .fault = uacce_vma_fault, .close = uacce_vma_close, }; -- cgit v1.2.3-59-g8ed1b From 04aaca197f16aff608e19ee98b1e55f535d746be Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 17 Jun 2020 17:33:13 +0900 Subject: char: raw: do not leak CONFIG_MAX_RAW_DEVS to userspace include/uapi/linux/raw.h leaks CONFIG_MAX_RAW_DEVS to userspace. Userspace programs cannot use MAX_RAW_MINORS since CONFIG_MAX_RAW_DEVS is not available anyway. Remove the MAX_RAW_MINORS definition from the exported header, and use CONFIG_MAX_RAW_DEVS in drivers/char/raw.c While I was here, I converted printk(KERN_WARNING ...) to pr_warn(...) and stretched the warning message. Signed-off-by: Masahiro Yamada Link: https://lore.kernel.org/r/20200617083313.183184-1-masahiroy@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/char/raw.c | 8 ++++---- include/uapi/linux/raw.h | 2 -- scripts/headers_install.sh | 1 - 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/char/raw.c b/drivers/char/raw.c index 3484e9145aea..380bf518338e 100644 --- a/drivers/char/raw.c +++ b/drivers/char/raw.c @@ -37,7 +37,7 @@ static struct raw_device_data *raw_devices; static DEFINE_MUTEX(raw_mutex); static const struct file_operations raw_ctl_fops; /* forward declaration */ -static int max_raw_minors = MAX_RAW_MINORS; +static int max_raw_minors = CONFIG_MAX_RAW_DEVS; module_param(max_raw_minors, int, 0); MODULE_PARM_DESC(max_raw_minors, "Maximum number of raw devices (1-65536)"); @@ -317,9 +317,9 @@ static int __init raw_init(void) int ret; if (max_raw_minors < 1 || max_raw_minors > 65536) { - printk(KERN_WARNING "raw: invalid max_raw_minors (must be" - " between 1 and 65536), using %d\n", MAX_RAW_MINORS); - max_raw_minors = MAX_RAW_MINORS; + pr_warn("raw: invalid max_raw_minors (must be between 1 and 65536), using %d\n", + CONFIG_MAX_RAW_DEVS); + max_raw_minors = CONFIG_MAX_RAW_DEVS; } raw_devices = vzalloc(array_size(max_raw_minors, diff --git a/include/uapi/linux/raw.h b/include/uapi/linux/raw.h index dc96dda479d6..47874919d0b9 100644 --- a/include/uapi/linux/raw.h +++ b/include/uapi/linux/raw.h @@ -14,6 +14,4 @@ struct raw_config_request __u64 block_minor; }; -#define MAX_RAW_MINORS CONFIG_MAX_RAW_DEVS - #endif /* __LINUX_RAW_H */ diff --git a/scripts/headers_install.sh b/scripts/headers_install.sh index 224f51012b6e..cdd66038818c 100755 --- a/scripts/headers_install.sh +++ b/scripts/headers_install.sh @@ -90,7 +90,6 @@ include/uapi/linux/elfcore.h:CONFIG_BINFMT_ELF_FDPIC include/uapi/linux/eventpoll.h:CONFIG_PM_SLEEP include/uapi/linux/hw_breakpoint.h:CONFIG_HAVE_MIXED_BREAKPOINTS_REGS include/uapi/linux/pktcdvd.h:CONFIG_CDROM_PKTCDVD_WCACHE -include/uapi/linux/raw.h:CONFIG_MAX_RAW_DEVS " for c in $configs -- cgit v1.2.3-59-g8ed1b From 877589350269e188cc19563745fd0f6dac14561b Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 11 Jun 2020 16:31:08 +0100 Subject: ttyprintk: remove redundant initialization of variable ret The variable ret is being initialized with a value that is never read and it is being updated later with a new value. The initialization is redundant and can be removed. Addresses-Coverity: ("Unused value") Signed-off-by: Colin Ian King Link: https://lore.kernel.org/r/20200611153108.927614-1-colin.king@canonical.com Signed-off-by: Greg Kroah-Hartman --- drivers/char/ttyprintk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/char/ttyprintk.c b/drivers/char/ttyprintk.c index 56db949a7b70..6a0059e508e3 100644 --- a/drivers/char/ttyprintk.c +++ b/drivers/char/ttyprintk.c @@ -172,7 +172,7 @@ static struct tty_driver *ttyprintk_driver; static int __init ttyprintk_init(void) { - int ret = -ENOMEM; + int ret; spin_lock_init(&tpk_port.spinlock); -- cgit v1.2.3-59-g8ed1b From 7ca78630a1ae082e6d4488884cd771ee4bb266ea Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 11 Jun 2020 16:27:08 +0100 Subject: char/mwave: remove redundant initialization of variable bRC The variable bRC is being initialized with a value that is never read and it is being updated later with a new value. The initialization is redundant and can be removed. Addresses-Coverity: ("Unused value") Signed-off-by: Colin Ian King Link: https://lore.kernel.org/r/20200611152708.927344-1-colin.king@canonical.com Signed-off-by: Greg Kroah-Hartman --- drivers/char/mwave/smapi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/char/mwave/smapi.c b/drivers/char/mwave/smapi.c index 691f5898bb32..f8d79d393b69 100644 --- a/drivers/char/mwave/smapi.c +++ b/drivers/char/mwave/smapi.c @@ -126,7 +126,7 @@ static int smapi_request(unsigned short inBX, unsigned short inCX, int smapi_query_DSP_cfg(SMAPI_DSP_SETTINGS * pSettings) { - int bRC = -EIO; + int bRC; unsigned short usAX, usBX, usCX, usDX, usDI, usSI; static const unsigned short ausDspBases[] = { 0x0030, 0x4E30, 0x8E30, 0xCE30, @@ -497,7 +497,7 @@ exit_smapi_request_error: int smapi_set_DSP_power_state(bool bOn) { - int bRC = -EIO; + int bRC; unsigned short usAX, usBX, usCX, usDX, usDI, usSI; unsigned short usPowerFunction; -- cgit v1.2.3-59-g8ed1b From 8965930c0b55bca1e49addd677bb3b2e8231f979 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 1 Jul 2020 10:36:16 +0100 Subject: misc: eeprom: at24: Tell the compiler that ACPI functions may not be used MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ... as is the case when !CONFIG_ACPI. Fixes the following W=1 kernel build warning: drivers/misc/eeprom/at24.c:228:36: warning: ‘at24_acpi_ids’ defined but not used [-Wunused-const-variable=] Signed-off-by: Lee Jones Acked-by: Bartosz Golaszewski Link: https://lore.kernel.org/r/20200701093616.GX1179328@dell Signed-off-by: Greg Kroah-Hartman --- drivers/misc/eeprom/at24.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c index 9ff18d4961ce..2591c21b2b5d 100644 --- a/drivers/misc/eeprom/at24.c +++ b/drivers/misc/eeprom/at24.c @@ -225,7 +225,7 @@ static const struct of_device_id at24_of_match[] = { }; MODULE_DEVICE_TABLE(of, at24_of_match); -static const struct acpi_device_id at24_acpi_ids[] = { +static const struct acpi_device_id __maybe_unused at24_acpi_ids[] = { { "INT3499", (kernel_ulong_t)&at24_data_INT3499 }, { "TPF0001", (kernel_ulong_t)&at24_data_24c1024 }, { /* END OF LIST */ } -- cgit v1.2.3-59-g8ed1b From 017d740e9e906e6c61619d6be661b4e2be942121 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 9 Jul 2020 07:56:51 +0100 Subject: misc: cxl: flash: Remove unused variable 'drc_index' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Keeping the pointer increment though. Fixes the following W=1 kernel build warning: drivers/misc/cxl/flash.c: In function ‘update_devicetree’: drivers/misc/cxl/flash.c:178:16: warning: variable ‘drc_index’ set but not used [-Wunused-but-set-variable] 178 | __be32 *data, drc_index, phandle; | ^~~~~~~~~ Cc: Frederic Barrat Cc: Andrew Donnellan Cc: linuxppc-dev@lists.ozlabs.org Signed-off-by: Lee Jones Acked-by: Andrew Donnellan Acked-by: Arnd Bergmann Link: https://lore.kernel.org/r/20200709065651.GY3500@dell Signed-off-by: Greg Kroah-Hartman --- drivers/misc/cxl/flash.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/misc/cxl/flash.c b/drivers/misc/cxl/flash.c index cb9cca35a226..5b93ff51d82a 100644 --- a/drivers/misc/cxl/flash.c +++ b/drivers/misc/cxl/flash.c @@ -175,7 +175,7 @@ static int update_devicetree(struct cxl *adapter, s32 scope) struct update_nodes_workarea *unwa; u32 action, node_count; int token, rc, i; - __be32 *data, drc_index, phandle; + __be32 *data, phandle; char *buf; token = rtas_token("ibm,update-nodes"); @@ -213,7 +213,7 @@ static int update_devicetree(struct cxl *adapter, s32 scope) break; case OPCODE_ADD: /* nothing to do, just move pointer */ - drc_index = *data++; + data++; break; } } -- cgit v1.2.3-59-g8ed1b From a9f91cebbeb8ea1355e852cce6d40efbcddbfe2b Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 8 Jul 2020 13:57:09 +0100 Subject: misc: vmw_vmci_defs: Mark 'struct vmci_handle VMCI_ANON_SRC_HANDLE' as __maybe_unused MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vmw_vmci_defs.h is included by multiple source files. Some of which do not make use of 'struct vmci_handle VMCI_ANON_SRC_HANDLE' rendering it unused. Ensure the compiler knows that this is in fact intentional by marking it as __maybe_unused. This fixes the following W=1 warnings: In file included from drivers/misc/vmw_vmci/vmci_context.c:8: include/linux/vmw_vmci_defs.h:162:33: warning: ‘VMCI_ANON_SRC_HANDLE’ defined but not used [-Wunused-const-variable=] 162 | static const struct vmci_handle VMCI_ANON_SRC_HANDLE = { | ^~~~~~~~~~~~~~~~~~~~ In file included from drivers/misc/vmw_vmci/vmci_datagram.c:8: include/linux/vmw_vmci_defs.h:162:33: warning: ‘VMCI_ANON_SRC_HANDLE’ defined but not used [-Wunused-const-variable=] 162 | static const struct vmci_handle VMCI_ANON_SRC_HANDLE = { | ^~~~~~~~~~~~~~~~~~~~ Cc: George Zhang Signed-off-by: Lee Jones Acked-by: Arnd Bergmann Link: https://lore.kernel.org/r/20200708125711.3443569-2-lee.jones@linaro.org Signed-off-by: Greg Kroah-Hartman --- include/linux/vmw_vmci_defs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/vmw_vmci_defs.h b/include/linux/vmw_vmci_defs.h index fefb5292403b..be0afe6f379b 100644 --- a/include/linux/vmw_vmci_defs.h +++ b/include/linux/vmw_vmci_defs.h @@ -159,7 +159,7 @@ static inline bool vmci_handle_is_invalid(struct vmci_handle h) */ #define VMCI_ANON_SRC_CONTEXT_ID VMCI_INVALID_ID #define VMCI_ANON_SRC_RESOURCE_ID VMCI_INVALID_ID -static const struct vmci_handle VMCI_ANON_SRC_HANDLE = { +static const struct vmci_handle __maybe_unused VMCI_ANON_SRC_HANDLE = { .context = VMCI_ANON_SRC_CONTEXT_ID, .resource = VMCI_ANON_SRC_RESOURCE_ID }; -- cgit v1.2.3-59-g8ed1b From 703275a867ca7674fa65b7dcebb0edfe43acf0a8 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Fri, 10 Jul 2020 07:34:49 -0400 Subject: vop: sparse warning fixup vop_dc_to_vdev dropped an __iomem tag on its argument, causing a sparse warning. Fix it up. Signed-off-by: Michael S. Tsirkin Link: https://lore.kernel.org/r/20200710113447.427927-1-mst@redhat.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/vop/vop_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/mic/vop/vop_main.c b/drivers/misc/mic/vop/vop_main.c index 7ce66890edad..55e7f21e51f4 100644 --- a/drivers/misc/mic/vop/vop_main.c +++ b/drivers/misc/mic/vop/vop_main.c @@ -546,7 +546,7 @@ static int vop_match_desc(struct device *dev, void *data) return vdev->desc == (void __iomem *)data; } -static struct _vop_vdev *vop_dc_to_vdev(struct mic_device_ctrl *dc) +static struct _vop_vdev *vop_dc_to_vdev(struct mic_device_ctrl __iomem *dc) { return (struct _vop_vdev *)(unsigned long)readq(&dc->vdev); } -- cgit v1.2.3-59-g8ed1b From 47681a4c0d8f47ccee6ce3ff10bd08e65c369e84 Mon Sep 17 00:00:00 2001 From: Suren Baghdasaryan Date: Wed, 8 Jul 2020 16:12:53 -0700 Subject: MAINTAINERS: add Hridya and myself into Android driver maintainers list Add new maintainers for ashmem driver to handle related issues. Signed-off-by: Suren Baghdasaryan Acked-by: Todd Kjos Link: https://lore.kernel.org/r/20200708231253.3831497-1-surenb@google.com Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 496fd4eafb68..07771576b0b1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1170,6 +1170,8 @@ M: Todd Kjos M: Martijn Coenen M: Joel Fernandes M: Christian Brauner +M: Hridya Valsaraju +M: Suren Baghdasaryan L: devel@driverdev.osuosl.org S: Supported T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging.git -- cgit v1.2.3-59-g8ed1b From c7d4a0b7114c5eeb7aba7df91d4ab5ed91adea25 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Fri, 3 Jul 2020 20:44:47 -0700 Subject: Documentation/driver-api: firmware/built-in-fw: drop doubled word Drop the doubled word "for". Signed-off-by: Randy Dunlap Cc: Jonathan Corbet Cc: linux-doc@vger.kernel.org Acked-by: Luis Chamberlain Link: https://lore.kernel.org/r/20200704034502.17199-3-rdunlap@infradead.org Signed-off-by: Greg Kroah-Hartman --- Documentation/driver-api/firmware/built-in-fw.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/driver-api/firmware/built-in-fw.rst b/Documentation/driver-api/firmware/built-in-fw.rst index 396cdf591ac5..bc1c961bace1 100644 --- a/Documentation/driver-api/firmware/built-in-fw.rst +++ b/Documentation/driver-api/firmware/built-in-fw.rst @@ -28,6 +28,6 @@ able to make use of built-in firmware: * Some firmware files may be really large in size. The remote-proc subsystem is an example subsystem which deals with these sorts of firmware * The firmware may need to be scraped out from some device specific location - dynamically, an example is calibration data for for some WiFi chipsets. This + dynamically, an example is calibration data for some WiFi chipsets. This calibration data can be unique per sold device. -- cgit v1.2.3-59-g8ed1b From 20b33f84f94dc435c62844b6fb3a1990da97e428 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Fri, 3 Jul 2020 20:44:48 -0700 Subject: Documentation/driver-api: firmware/firmware_cache: drop doubled word Drop the doubled word "if". Signed-off-by: Randy Dunlap Cc: Jonathan Corbet Cc: linux-doc@vger.kernel.org Acked-by: Luis Chamberlain Link: https://lore.kernel.org/r/20200704034502.17199-4-rdunlap@infradead.org Signed-off-by: Greg Kroah-Hartman --- Documentation/driver-api/firmware/firmware_cache.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/driver-api/firmware/firmware_cache.rst b/Documentation/driver-api/firmware/firmware_cache.rst index c2e69d9c6bf1..417b9e8347f0 100644 --- a/Documentation/driver-api/firmware/firmware_cache.rst +++ b/Documentation/driver-api/firmware/firmware_cache.rst @@ -27,7 +27,7 @@ Some implementation details about the firmware cache setup: uses all synchronous call except :c:func:`request_firmware_into_buf`. * If an asynchronous call is used the firmware cache is only set up for a - device if if the second argument (uevent) to request_firmware_nowait() is + device if the second argument (uevent) to request_firmware_nowait() is true. When uevent is true it requests that a kobject uevent be sent to userspace for the firmware request through the sysfs fallback mechanism if the firmware file is not found. -- cgit v1.2.3-59-g8ed1b From e2e4e8f5ac69ab8b50c00243172d79ee67aa6b69 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Fri, 3 Jul 2020 20:44:49 -0700 Subject: Documentation/driver-api: firmware/direct-fs-lookup: drop doubled word Drop the doubled word "firmware". Signed-off-by: Randy Dunlap Cc: Jonathan Corbet Cc: linux-doc@vger.kernel.org Acked-by: Luis Chamberlain Link: https://lore.kernel.org/r/20200704034502.17199-5-rdunlap@infradead.org Signed-off-by: Greg Kroah-Hartman --- Documentation/driver-api/firmware/direct-fs-lookup.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/driver-api/firmware/direct-fs-lookup.rst b/Documentation/driver-api/firmware/direct-fs-lookup.rst index 82b4d585a213..e04353d1b06b 100644 --- a/Documentation/driver-api/firmware/direct-fs-lookup.rst +++ b/Documentation/driver-api/firmware/direct-fs-lookup.rst @@ -24,7 +24,7 @@ available. Stuffing the firmware into initramfs resolves this race issue, however note that using initrd does not suffice to address the same race. There are circumstances that justify not wanting to include firmware into -initramfs, such as dealing with large firmware firmware files for the +initramfs, such as dealing with large firmware files for the remote-proc subsystem. For such cases using a userspace fallback mechanism is currently the only viable solution as only userspace can know for sure when the real rootfs is ready and mounted. -- cgit v1.2.3-59-g8ed1b From 726b5bd3cf9705f1beea872c093ecdcc2376d477 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Fri, 3 Jul 2020 20:44:50 -0700 Subject: Documentation/driver-api: firmware/request_firmware: drop doubled word Drop the doubled word "call". Signed-off-by: Randy Dunlap Cc: Jonathan Corbet Cc: linux-doc@vger.kernel.org Acked-by: Luis Chamberlain Link: https://lore.kernel.org/r/20200704034502.17199-6-rdunlap@infradead.org Signed-off-by: Greg Kroah-Hartman --- Documentation/driver-api/firmware/request_firmware.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/driver-api/firmware/request_firmware.rst b/Documentation/driver-api/firmware/request_firmware.rst index cd076462d235..0d6ea0329995 100644 --- a/Documentation/driver-api/firmware/request_firmware.rst +++ b/Documentation/driver-api/firmware/request_firmware.rst @@ -76,5 +76,5 @@ firmware. For example if you used request_firmware() and it returns, the driver has the firmware image accessible in fw_entry->{data,size}. If something went wrong request_firmware() returns non-zero and fw_entry is set to NULL. Once your driver is done with processing the firmware it -can call call release_firmware(fw_entry) to release the firmware image +can call release_firmware(fw_entry) to release the firmware image and any related resource. -- cgit v1.2.3-59-g8ed1b From 1682986df00ce3577f4ba45d0241228ea724da04 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Fri, 3 Jul 2020 20:44:59 -0700 Subject: Documentation/driver-api: uio-howto: drop doubled word Drop the doubled word "you". Signed-off-by: Randy Dunlap Cc: Jonathan Corbet Cc: linux-doc@vger.kernel.org Cc: Greg Kroah-Hartman Link: https://lore.kernel.org/r/20200704034502.17199-15-rdunlap@infradead.org Signed-off-by: Greg Kroah-Hartman --- Documentation/driver-api/uio-howto.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/driver-api/uio-howto.rst b/Documentation/driver-api/uio-howto.rst index 84091cd25dc4..907ffa3b38f5 100644 --- a/Documentation/driver-api/uio-howto.rst +++ b/Documentation/driver-api/uio-howto.rst @@ -274,7 +274,7 @@ fields of ``struct uio_mem``: region, it will show up in the corresponding sysfs node. - ``int memtype``: Required if the mapping is used. Set this to - ``UIO_MEM_PHYS`` if you you have physical memory on your card to be + ``UIO_MEM_PHYS`` if you have physical memory on your card to be mapped. Use ``UIO_MEM_LOGICAL`` for logical memory (e.g. allocated with :c:func:`__get_free_pages()` but not kmalloc()). There's also ``UIO_MEM_VIRTUAL`` for virtual memory. -- cgit v1.2.3-59-g8ed1b From f31a03b123b1f849bc0f60493edb95342f2c8e9a Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Fri, 3 Jul 2020 20:45:02 -0700 Subject: Documentation/driver-api: xillybus: drop doubled word Drop the doubled word "the". Signed-off-by: Randy Dunlap Cc: Jonathan Corbet Cc: linux-doc@vger.kernel.org Acked-by: Eli Billauer Link: https://lore.kernel.org/r/20200704034502.17199-18-rdunlap@infradead.org Signed-off-by: Greg Kroah-Hartman --- Documentation/driver-api/xillybus.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/driver-api/xillybus.rst b/Documentation/driver-api/xillybus.rst index 2446ee303c09..a3ab832cb22b 100644 --- a/Documentation/driver-api/xillybus.rst +++ b/Documentation/driver-api/xillybus.rst @@ -273,7 +273,7 @@ buffer is full, the FPGA informs the host about that (appending a XILLYMSG_OPCODE_RELEASEBUF message channel 0 and sending an interrupt if necessary). The host responds by making the data available for reading through the character device. When all data has been read, the host writes on the -the FPGA's buffer control register, allowing the buffer's overwriting. Flow +FPGA's buffer control register, allowing the buffer's overwriting. Flow control mechanisms exist on both sides to prevent underflows and overflows. This is not good enough for creating a TCP/IP-like stream: If the data flow -- cgit v1.2.3-59-g8ed1b From 849a9366cba92cb5dc9dc1161ef49416a290aae9 Mon Sep 17 00:00:00 2001 From: Ricky Wu Date: Mon, 6 Jul 2020 15:02:59 +0800 Subject: misc: rtsx: Add support new chip rts5228 mmc: rtsx: Add support MMC_CAP2_NO_MMC In order to support new chip rts5228, the definitions of some internal registers and workflow have to be modified. Added rts5228.c rts5228.h for independent functions of the new chip rts5228 Signed-off-by: Ricky Wu Link: https://lore.kernel.org/r/20200706070259.32565-1-ricky_wu@realtek.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/cardreader/Makefile | 2 +- drivers/misc/cardreader/rts5228.c | 740 +++++++++++++++++++++++++++++++++++++ drivers/misc/cardreader/rts5228.h | 168 +++++++++ drivers/misc/cardreader/rtsx_pcr.c | 89 +++-- drivers/misc/cardreader/rtsx_pcr.h | 5 + drivers/mmc/host/rtsx_pci_sdmmc.c | 2 + include/linux/rtsx_pci.h | 11 + 7 files changed, 985 insertions(+), 32 deletions(-) create mode 100644 drivers/misc/cardreader/rts5228.c create mode 100644 drivers/misc/cardreader/rts5228.h diff --git a/drivers/misc/cardreader/Makefile b/drivers/misc/cardreader/Makefile index 1f56267ed2f4..895128475d83 100644 --- a/drivers/misc/cardreader/Makefile +++ b/drivers/misc/cardreader/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_MISC_ALCOR_PCI) += alcor_pci.o obj-$(CONFIG_MISC_RTSX_PCI) += rtsx_pci.o -rtsx_pci-objs := rtsx_pcr.o rts5209.o rts5229.o rtl8411.o rts5227.o rts5249.o rts5260.o rts5261.o +rtsx_pci-objs := rtsx_pcr.o rts5209.o rts5229.o rtl8411.o rts5227.o rts5249.o rts5260.o rts5261.o rts5228.o obj-$(CONFIG_MISC_RTSX_USB) += rtsx_usb.o diff --git a/drivers/misc/cardreader/rts5228.c b/drivers/misc/cardreader/rts5228.c new file mode 100644 index 000000000000..99aff7cd0a93 --- /dev/null +++ b/drivers/misc/cardreader/rts5228.c @@ -0,0 +1,740 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* Driver for Realtek PCI-Express card reader + * + * Copyright(c) 2018-2019 Realtek Semiconductor Corp. All rights reserved. + * + * Author: + * Ricky WU + * Rui FENG + * Wei WANG + */ + +#include +#include +#include + +#include "rts5228.h" +#include "rtsx_pcr.h" + +static u8 rts5228_get_ic_version(struct rtsx_pcr *pcr) +{ + u8 val; + + rtsx_pci_read_register(pcr, DUMMY_REG_RESET_0, &val); + return val & IC_VERSION_MASK; +} + +static void rts5228_fill_driving(struct rtsx_pcr *pcr, u8 voltage) +{ + u8 driving_3v3[4][3] = { + {0x13, 0x13, 0x13}, + {0x96, 0x96, 0x96}, + {0x7F, 0x7F, 0x7F}, + {0x96, 0x96, 0x96}, + }; + u8 driving_1v8[4][3] = { + {0x99, 0x99, 0x99}, + {0xB5, 0xB5, 0xB5}, + {0xE6, 0x7E, 0xFE}, + {0x6B, 0x6B, 0x6B}, + }; + u8 (*driving)[3], drive_sel; + + if (voltage == OUTPUT_3V3) { + driving = driving_3v3; + drive_sel = pcr->sd30_drive_sel_3v3; + } else { + driving = driving_1v8; + drive_sel = pcr->sd30_drive_sel_1v8; + } + + rtsx_pci_write_register(pcr, SD30_CLK_DRIVE_SEL, + 0xFF, driving[drive_sel][0]); + + rtsx_pci_write_register(pcr, SD30_CMD_DRIVE_SEL, + 0xFF, driving[drive_sel][1]); + + rtsx_pci_write_register(pcr, SD30_DAT_DRIVE_SEL, + 0xFF, driving[drive_sel][2]); +} + +static void rtsx5228_fetch_vendor_settings(struct rtsx_pcr *pcr) +{ + u32 reg; + /* 0x724~0x727 */ + rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG1, ®); + pcr_dbg(pcr, "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG1, reg); + + if (!rtsx_vendor_setting_valid(reg)) { + pcr_dbg(pcr, "skip fetch vendor setting\n"); + return; + } + pcr->sd30_drive_sel_1v8 = rtsx_reg_to_sd30_drive_sel_1v8(reg); + pcr->aspm_en = rtsx_reg_to_aspm(reg); + + /* 0x814~0x817 */ + rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG2, ®); + pcr_dbg(pcr, "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG2, reg); + + pcr->rtd3_en = rtsx_reg_to_rtd3(reg); + if (rtsx_check_mmc_support(reg)) + pcr->extra_caps |= EXTRA_CAPS_NO_MMC; + pcr->sd30_drive_sel_3v3 = rtsx_reg_to_sd30_drive_sel_3v3(reg); + if (rtsx_reg_check_reverse_socket(reg)) + pcr->flags |= PCR_REVERSE_SOCKET; +} + +static int rts5228_optimize_phy(struct rtsx_pcr *pcr) +{ + return rtsx_pci_write_phy_register(pcr, 0x07, 0x8F40); +} + +static void rts5228_force_power_down(struct rtsx_pcr *pcr, u8 pm_state) +{ + /* Set relink_time to 0 */ + rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 1, MASK_8_BIT_DEF, 0); + rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 2, MASK_8_BIT_DEF, 0); + rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 3, + RELINK_TIME_MASK, 0); + + if (pm_state == HOST_ENTER_S3) + rtsx_pci_write_register(pcr, pcr->reg_pm_ctrl3, + D3_DELINK_MODE_EN, D3_DELINK_MODE_EN); + + rtsx_pci_write_register(pcr, FPDCTL, + SSC_POWER_DOWN, SSC_POWER_DOWN); +} + +static int rts5228_enable_auto_blink(struct rtsx_pcr *pcr) +{ + return rtsx_pci_write_register(pcr, OLT_LED_CTL, + LED_SHINE_MASK, LED_SHINE_EN); +} + +static int rts5228_disable_auto_blink(struct rtsx_pcr *pcr) +{ + return rtsx_pci_write_register(pcr, OLT_LED_CTL, + LED_SHINE_MASK, LED_SHINE_DISABLE); +} + +static int rts5228_turn_on_led(struct rtsx_pcr *pcr) +{ + return rtsx_pci_write_register(pcr, GPIO_CTL, + 0x02, 0x02); +} + +static int rts5228_turn_off_led(struct rtsx_pcr *pcr) +{ + return rtsx_pci_write_register(pcr, GPIO_CTL, + 0x02, 0x00); +} + +/* SD Pull Control Enable: + * SD_DAT[3:0] ==> pull up + * SD_CD ==> pull up + * SD_WP ==> pull up + * SD_CMD ==> pull up + * SD_CLK ==> pull down + */ +static const u32 rts5228_sd_pull_ctl_enable_tbl[] = { + RTSX_REG_PAIR(CARD_PULL_CTL2, 0xAA), + RTSX_REG_PAIR(CARD_PULL_CTL3, 0xE9), + 0, +}; + +/* SD Pull Control Disable: + * SD_DAT[3:0] ==> pull down + * SD_CD ==> pull up + * SD_WP ==> pull down + * SD_CMD ==> pull down + * SD_CLK ==> pull down + */ +static const u32 rts5228_sd_pull_ctl_disable_tbl[] = { + RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55), + RTSX_REG_PAIR(CARD_PULL_CTL3, 0xD5), + 0, +}; + +static int rts5228_sd_set_sample_push_timing_sd30(struct rtsx_pcr *pcr) +{ + rtsx_pci_write_register(pcr, SD_CFG1, SD_MODE_SELECT_MASK + | SD_ASYNC_FIFO_NOT_RST, SD_30_MODE | SD_ASYNC_FIFO_NOT_RST); + rtsx_pci_write_register(pcr, CLK_CTL, CLK_LOW_FREQ, CLK_LOW_FREQ); + rtsx_pci_write_register(pcr, CARD_CLK_SOURCE, 0xFF, + CRC_VAR_CLK0 | SD30_FIX_CLK | SAMPLE_VAR_CLK1); + rtsx_pci_write_register(pcr, CLK_CTL, CLK_LOW_FREQ, 0); + + return 0; +} + +static int rts5228_card_power_on(struct rtsx_pcr *pcr, int card) +{ + struct rtsx_cr_option *option = &pcr->option; + + if (option->ocp_en) + rtsx_pci_enable_ocp(pcr); + + rtsx_pci_write_register(pcr, REG_CRC_DUMMY_0, + CFG_SD_POW_AUTO_PD, CFG_SD_POW_AUTO_PD); + + rtsx_pci_write_register(pcr, RTS5228_LDO1_CFG1, + RTS5228_LDO1_TUNE_MASK, RTS5228_LDO1_33); + + rtsx_pci_write_register(pcr, RTS5228_LDO1233318_POW_CTL, + RTS5228_LDO1_POWERON_MASK, RTS5228_LDO1_SOFTSTART); + mdelay(2); + rtsx_pci_write_register(pcr, RTS5228_LDO1233318_POW_CTL, + RTS5228_LDO1_POWERON_MASK, RTS5228_LDO1_FULLON); + + + rtsx_pci_write_register(pcr, RTS5228_LDO1233318_POW_CTL, + RTS5228_LDO3318_POWERON, RTS5228_LDO3318_POWERON); + + msleep(20); + + rtsx_pci_write_register(pcr, CARD_OE, SD_OUTPUT_EN, SD_OUTPUT_EN); + + /* Initialize SD_CFG1 register */ + rtsx_pci_write_register(pcr, SD_CFG1, 0xFF, + SD_CLK_DIVIDE_128 | SD_20_MODE | SD_BUS_WIDTH_1BIT); + + rtsx_pci_write_register(pcr, SD_SAMPLE_POINT_CTL, + 0xFF, SD20_RX_POS_EDGE); + rtsx_pci_write_register(pcr, SD_PUSH_POINT_CTL, 0xFF, 0); + rtsx_pci_write_register(pcr, CARD_STOP, SD_STOP | SD_CLR_ERR, + SD_STOP | SD_CLR_ERR); + + /* Reset SD_CFG3 register */ + rtsx_pci_write_register(pcr, SD_CFG3, SD30_CLK_END_EN, 0); + rtsx_pci_write_register(pcr, REG_SD_STOP_SDCLK_CFG, + SD30_CLK_STOP_CFG_EN | SD30_CLK_STOP_CFG1 | + SD30_CLK_STOP_CFG0, 0); + + if (pcr->extra_caps & EXTRA_CAPS_SD_SDR50 || + pcr->extra_caps & EXTRA_CAPS_SD_SDR104) + rts5228_sd_set_sample_push_timing_sd30(pcr); + + return 0; +} + +static int rts5228_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage) +{ + int err; + u16 val = 0; + + rtsx_pci_write_register(pcr, RTS5228_CARD_PWR_CTL, + RTS5228_PUPDC, RTS5228_PUPDC); + + switch (voltage) { + case OUTPUT_3V3: + rtsx_pci_read_phy_register(pcr, PHY_TUNE, &val); + val |= PHY_TUNE_SDBUS_33; + err = rtsx_pci_write_phy_register(pcr, PHY_TUNE, val); + if (err < 0) + return err; + + rtsx_pci_write_register(pcr, RTS5228_DV3318_CFG, + RTS5228_DV3318_TUNE_MASK, RTS5228_DV3318_33); + rtsx_pci_write_register(pcr, SD_PAD_CTL, + SD_IO_USING_1V8, 0); + break; + case OUTPUT_1V8: + rtsx_pci_read_phy_register(pcr, PHY_TUNE, &val); + val &= ~PHY_TUNE_SDBUS_33; + err = rtsx_pci_write_phy_register(pcr, PHY_TUNE, val); + if (err < 0) + return err; + + rtsx_pci_write_register(pcr, RTS5228_DV3318_CFG, + RTS5228_DV3318_TUNE_MASK, RTS5228_DV3318_18); + rtsx_pci_write_register(pcr, SD_PAD_CTL, + SD_IO_USING_1V8, SD_IO_USING_1V8); + break; + default: + return -EINVAL; + } + + /* set pad drive */ + rts5228_fill_driving(pcr, voltage); + + return 0; +} + +static void rts5228_stop_cmd(struct rtsx_pcr *pcr) +{ + rtsx_pci_writel(pcr, RTSX_HCBCTLR, STOP_CMD); + rtsx_pci_writel(pcr, RTSX_HDBCTLR, STOP_DMA); + rtsx_pci_write_register(pcr, RTS5260_DMA_RST_CTL_0, + RTS5260_DMA_RST | RTS5260_ADMA3_RST, + RTS5260_DMA_RST | RTS5260_ADMA3_RST); + rtsx_pci_write_register(pcr, RBCTL, RB_FLUSH, RB_FLUSH); +} + +static void rts5228_card_before_power_off(struct rtsx_pcr *pcr) +{ + rts5228_stop_cmd(pcr); + rts5228_switch_output_voltage(pcr, OUTPUT_3V3); +} + +static void rts5228_enable_ocp(struct rtsx_pcr *pcr) +{ + u8 val = 0; + + val = SD_OCP_INT_EN | SD_DETECT_EN; + rtsx_pci_write_register(pcr, REG_OCPCTL, 0xFF, val); + rtsx_pci_write_register(pcr, RTS5228_LDO1_CFG0, + RTS5228_LDO1_OCP_EN | RTS5228_LDO1_OCP_LMT_EN, + RTS5228_LDO1_OCP_EN | RTS5228_LDO1_OCP_LMT_EN); +} + +static void rts5228_disable_ocp(struct rtsx_pcr *pcr) +{ + u8 mask = 0; + + mask = SD_OCP_INT_EN | SD_DETECT_EN; + rtsx_pci_write_register(pcr, REG_OCPCTL, mask, 0); + rtsx_pci_write_register(pcr, RTS5228_LDO1_CFG0, + RTS5228_LDO1_OCP_EN | RTS5228_LDO1_OCP_LMT_EN, 0); +} + +static int rts5228_card_power_off(struct rtsx_pcr *pcr, int card) +{ + int err = 0; + + rts5228_card_before_power_off(pcr); + err = rtsx_pci_write_register(pcr, RTS5228_LDO1233318_POW_CTL, + RTS5228_LDO_POWERON_MASK, 0); + rtsx_pci_write_register(pcr, REG_CRC_DUMMY_0, CFG_SD_POW_AUTO_PD, 0); + + if (pcr->option.ocp_en) + rtsx_pci_disable_ocp(pcr); + + return err; +} + +static void rts5228_init_ocp(struct rtsx_pcr *pcr) +{ + struct rtsx_cr_option *option = &pcr->option; + + if (option->ocp_en) { + u8 mask, val; + + rtsx_pci_write_register(pcr, RTS5228_LDO1_CFG0, + RTS5228_LDO1_OCP_EN | RTS5228_LDO1_OCP_LMT_EN, + RTS5228_LDO1_OCP_EN | RTS5228_LDO1_OCP_LMT_EN); + + rtsx_pci_write_register(pcr, RTS5228_LDO1_CFG0, + RTS5228_LDO1_OCP_THD_MASK, option->sd_800mA_ocp_thd); + + rtsx_pci_write_register(pcr, RTS5228_LDO1_CFG0, + RTS5228_LDO1_OCP_LMT_THD_MASK, + RTS5228_LDO1_LMT_THD_1500); + + rtsx_pci_read_register(pcr, RTS5228_LDO1_CFG0, &val); + + mask = SD_OCP_GLITCH_MASK; + val = pcr->hw_param.ocp_glitch; + rtsx_pci_write_register(pcr, REG_OCPGLITCH, mask, val); + + rts5228_enable_ocp(pcr); + + } else { + rtsx_pci_write_register(pcr, RTS5228_LDO1_CFG0, + RTS5228_LDO1_OCP_EN | RTS5228_LDO1_OCP_LMT_EN, 0); + } +} + +static void rts5228_clear_ocpstat(struct rtsx_pcr *pcr) +{ + u8 mask = 0; + u8 val = 0; + + mask = SD_OCP_INT_CLR | SD_OC_CLR; + val = SD_OCP_INT_CLR | SD_OC_CLR; + + rtsx_pci_write_register(pcr, REG_OCPCTL, mask, val); + + udelay(1000); + rtsx_pci_write_register(pcr, REG_OCPCTL, mask, 0); + +} + +static void rts5228_process_ocp(struct rtsx_pcr *pcr) +{ + if (!pcr->option.ocp_en) + return; + + rtsx_pci_get_ocpstat(pcr, &pcr->ocp_stat); + + if (pcr->ocp_stat & (SD_OC_NOW | SD_OC_EVER)) { + rts5228_clear_ocpstat(pcr); + rts5228_card_power_off(pcr, RTSX_SD_CARD); + rtsx_pci_write_register(pcr, CARD_OE, SD_OUTPUT_EN, 0); + pcr->ocp_stat = 0; + } + +} + +static void rts5228_init_from_cfg(struct rtsx_pcr *pcr) +{ + u32 lval; + struct rtsx_cr_option *option = &pcr->option; + + rtsx_pci_read_config_dword(pcr, PCR_ASPM_SETTING_REG1, &lval); + + + if (0 == (lval & 0x0F)) + rtsx_pci_enable_oobs_polling(pcr); + else + rtsx_pci_disable_oobs_polling(pcr); + + if (lval & ASPM_L1_1_EN_MASK) + rtsx_set_dev_flag(pcr, ASPM_L1_1_EN); + else + rtsx_clear_dev_flag(pcr, ASPM_L1_1_EN); + + if (lval & ASPM_L1_2_EN_MASK) + rtsx_set_dev_flag(pcr, ASPM_L1_2_EN); + else + rtsx_clear_dev_flag(pcr, ASPM_L1_2_EN); + + if (lval & PM_L1_1_EN_MASK) + rtsx_set_dev_flag(pcr, PM_L1_1_EN); + else + rtsx_clear_dev_flag(pcr, PM_L1_1_EN); + + if (lval & PM_L1_2_EN_MASK) + rtsx_set_dev_flag(pcr, PM_L1_2_EN); + else + rtsx_clear_dev_flag(pcr, PM_L1_2_EN); + + rtsx_pci_write_register(pcr, ASPM_FORCE_CTL, 0xFF, 0); + if (option->ltr_en) { + u16 val; + + pcie_capability_read_word(pcr->pci, PCI_EXP_DEVCTL2, &val); + if (val & PCI_EXP_DEVCTL2_LTR_EN) { + option->ltr_enabled = true; + option->ltr_active = true; + rtsx_set_ltr_latency(pcr, option->ltr_active_latency); + } else { + option->ltr_enabled = false; + } + } + + if (rtsx_check_dev_flag(pcr, ASPM_L1_1_EN | ASPM_L1_2_EN + | PM_L1_1_EN | PM_L1_2_EN)) + option->force_clkreq_0 = false; + else + option->force_clkreq_0 = true; +} + +static int rts5228_extra_init_hw(struct rtsx_pcr *pcr) +{ + struct rtsx_cr_option *option = &pcr->option; + + rtsx_pci_write_register(pcr, RTS5228_AUTOLOAD_CFG1, + CD_RESUME_EN_MASK, CD_RESUME_EN_MASK); + + rts5228_init_from_cfg(pcr); + + rtsx_pci_write_register(pcr, L1SUB_CONFIG1, + AUX_CLK_ACTIVE_SEL_MASK, MAC_CKSW_DONE); + rtsx_pci_write_register(pcr, L1SUB_CONFIG3, 0xFF, 0); + + rtsx_pci_write_register(pcr, FUNC_FORCE_CTL, + FUNC_FORCE_UPME_XMT_DBG, FUNC_FORCE_UPME_XMT_DBG); + + rtsx_pci_write_register(pcr, PCLK_CTL, + PCLK_MODE_SEL, PCLK_MODE_SEL); + + rtsx_pci_write_register(pcr, PM_EVENT_DEBUG, PME_DEBUG_0, PME_DEBUG_0); + rtsx_pci_write_register(pcr, PM_CLK_FORCE_CTL, CLK_PM_EN, CLK_PM_EN); + + /* LED shine disabled, set initial shine cycle period */ + rtsx_pci_write_register(pcr, OLT_LED_CTL, 0x0F, 0x02); + + /* Configure driving */ + rts5228_fill_driving(pcr, OUTPUT_3V3); + + if (pcr->flags & PCR_REVERSE_SOCKET) + rtsx_pci_write_register(pcr, PETXCFG, 0x30, 0x30); + else + rtsx_pci_write_register(pcr, PETXCFG, 0x30, 0x00); + + /* + * If u_force_clkreq_0 is enabled, CLKREQ# PIN will be forced + * to drive low, and we forcibly request clock. + */ + if (option->force_clkreq_0) + rtsx_pci_write_register(pcr, PETXCFG, + FORCE_CLKREQ_DELINK_MASK, FORCE_CLKREQ_LOW); + else + rtsx_pci_write_register(pcr, PETXCFG, + FORCE_CLKREQ_DELINK_MASK, FORCE_CLKREQ_HIGH); + + rtsx_pci_write_register(pcr, PWD_SUSPEND_EN, 0xFF, 0xFB); + rtsx_pci_write_register(pcr, pcr->reg_pm_ctrl3, 0x10, 0x00); + rtsx_pci_write_register(pcr, RTS5228_REG_PME_FORCE_CTL, + FORCE_PM_CONTROL | FORCE_PM_VALUE, FORCE_PM_CONTROL); + + return 0; +} + +static void rts5228_enable_aspm(struct rtsx_pcr *pcr, bool enable) +{ + u8 mask, val; + + if (pcr->aspm_enabled == enable) + return; + + mask = FORCE_ASPM_VAL_MASK | FORCE_ASPM_CTL0 | FORCE_ASPM_CTL1; + val = FORCE_ASPM_CTL0 | FORCE_ASPM_CTL1; + val |= (pcr->aspm_en & 0x02); + rtsx_pci_write_register(pcr, ASPM_FORCE_CTL, mask, val); + pcie_capability_clear_and_set_word(pcr->pci, PCI_EXP_LNKCTL, + PCI_EXP_LNKCTL_ASPMC, pcr->aspm_en); + pcr->aspm_enabled = enable; +} + +static void rts5228_disable_aspm(struct rtsx_pcr *pcr, bool enable) +{ + u8 mask, val; + + if (pcr->aspm_enabled == enable) + return; + + pcie_capability_clear_and_set_word(pcr->pci, PCI_EXP_LNKCTL, + PCI_EXP_LNKCTL_ASPMC, 0); + mask = FORCE_ASPM_VAL_MASK | FORCE_ASPM_CTL0 | FORCE_ASPM_CTL1; + val = FORCE_ASPM_CTL0 | FORCE_ASPM_CTL1; + rtsx_pci_write_register(pcr, ASPM_FORCE_CTL, mask, val); + rtsx_pci_write_register(pcr, SD_CFG1, SD_ASYNC_FIFO_NOT_RST, 0); + mdelay(10); + pcr->aspm_enabled = enable; +} + +static void rts5228_set_aspm(struct rtsx_pcr *pcr, bool enable) +{ + if (enable) + rts5228_enable_aspm(pcr, true); + else + rts5228_disable_aspm(pcr, false); +} + +static void rts5228_set_l1off_cfg_sub_d0(struct rtsx_pcr *pcr, int active) +{ + struct rtsx_cr_option *option = &pcr->option; + int aspm_L1_1, aspm_L1_2; + u8 val = 0; + + aspm_L1_1 = rtsx_check_dev_flag(pcr, ASPM_L1_1_EN); + aspm_L1_2 = rtsx_check_dev_flag(pcr, ASPM_L1_2_EN); + + if (active) { + /* run, latency: 60us */ + if (aspm_L1_1) + val = option->ltr_l1off_snooze_sspwrgate; + } else { + /* l1off, latency: 300us */ + if (aspm_L1_2) + val = option->ltr_l1off_sspwrgate; + } + + rtsx_set_l1off_sub(pcr, val); +} + +static const struct pcr_ops rts5228_pcr_ops = { + .fetch_vendor_settings = rtsx5228_fetch_vendor_settings, + .turn_on_led = rts5228_turn_on_led, + .turn_off_led = rts5228_turn_off_led, + .extra_init_hw = rts5228_extra_init_hw, + .enable_auto_blink = rts5228_enable_auto_blink, + .disable_auto_blink = rts5228_disable_auto_blink, + .card_power_on = rts5228_card_power_on, + .card_power_off = rts5228_card_power_off, + .switch_output_voltage = rts5228_switch_output_voltage, + .force_power_down = rts5228_force_power_down, + .stop_cmd = rts5228_stop_cmd, + .set_aspm = rts5228_set_aspm, + .set_l1off_cfg_sub_d0 = rts5228_set_l1off_cfg_sub_d0, + .enable_ocp = rts5228_enable_ocp, + .disable_ocp = rts5228_disable_ocp, + .init_ocp = rts5228_init_ocp, + .process_ocp = rts5228_process_ocp, + .clear_ocpstat = rts5228_clear_ocpstat, + .optimize_phy = rts5228_optimize_phy, +}; + + +static inline u8 double_ssc_depth(u8 depth) +{ + return ((depth > 1) ? (depth - 1) : depth); +} + +int rts5228_pci_switch_clock(struct rtsx_pcr *pcr, unsigned int card_clock, + u8 ssc_depth, bool initial_mode, bool double_clk, bool vpclk) +{ + int err, clk; + u16 n; + u8 clk_divider, mcu_cnt, div; + static const u8 depth[] = { + [RTSX_SSC_DEPTH_4M] = RTS5228_SSC_DEPTH_4M, + [RTSX_SSC_DEPTH_2M] = RTS5228_SSC_DEPTH_2M, + [RTSX_SSC_DEPTH_1M] = RTS5228_SSC_DEPTH_1M, + [RTSX_SSC_DEPTH_500K] = RTS5228_SSC_DEPTH_512K, + }; + + if (initial_mode) { + /* We use 250k(around) here, in initial stage */ + clk_divider = SD_CLK_DIVIDE_128; + card_clock = 30000000; + } else { + clk_divider = SD_CLK_DIVIDE_0; + } + err = rtsx_pci_write_register(pcr, SD_CFG1, + SD_CLK_DIVIDE_MASK, clk_divider); + if (err < 0) + return err; + + card_clock /= 1000000; + pcr_dbg(pcr, "Switch card clock to %dMHz\n", card_clock); + + clk = card_clock; + if (!initial_mode && double_clk) + clk = card_clock * 2; + pcr_dbg(pcr, "Internal SSC clock: %dMHz (cur_clock = %d)\n", + clk, pcr->cur_clock); + + if (clk == pcr->cur_clock) + return 0; + + if (pcr->ops->conv_clk_and_div_n) + n = pcr->ops->conv_clk_and_div_n(clk, CLK_TO_DIV_N); + else + n = clk - 4; + if ((clk <= 4) || (n > 396)) + return -EINVAL; + + mcu_cnt = 125/clk + 3; + if (mcu_cnt > 15) + mcu_cnt = 15; + + div = CLK_DIV_1; + while ((n < MIN_DIV_N_PCR - 4) && (div < CLK_DIV_8)) { + if (pcr->ops->conv_clk_and_div_n) { + int dbl_clk = pcr->ops->conv_clk_and_div_n(n, + DIV_N_TO_CLK) * 2; + n = pcr->ops->conv_clk_and_div_n(dbl_clk, + CLK_TO_DIV_N); + } else { + n = (n + 4) * 2 - 4; + } + div++; + } + + n = (n / 2) - 1; + pcr_dbg(pcr, "n = %d, div = %d\n", n, div); + + ssc_depth = depth[ssc_depth]; + if (double_clk) + ssc_depth = double_ssc_depth(ssc_depth); + + if (ssc_depth) { + if (div == CLK_DIV_2) { + if (ssc_depth > 1) + ssc_depth -= 1; + else + ssc_depth = RTS5228_SSC_DEPTH_8M; + } else if (div == CLK_DIV_4) { + if (ssc_depth > 2) + ssc_depth -= 2; + else + ssc_depth = RTS5228_SSC_DEPTH_8M; + } else if (div == CLK_DIV_8) { + if (ssc_depth > 3) + ssc_depth -= 3; + else + ssc_depth = RTS5228_SSC_DEPTH_8M; + } + } else { + ssc_depth = 0; + } + pcr_dbg(pcr, "ssc_depth = %d\n", ssc_depth); + + rtsx_pci_init_cmd(pcr); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_CTL, + CLK_LOW_FREQ, CLK_LOW_FREQ); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_DIV, + 0xFF, (div << 4) | mcu_cnt); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL1, SSC_RSTB, 0); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL2, + SSC_DEPTH_MASK, ssc_depth); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_DIV_N_0, 0xFF, n); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL1, SSC_RSTB, SSC_RSTB); + if (vpclk) { + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_VPCLK0_CTL, + PHASE_NOT_RESET, 0); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_VPCLK1_CTL, + PHASE_NOT_RESET, 0); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_VPCLK0_CTL, + PHASE_NOT_RESET, PHASE_NOT_RESET); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_VPCLK1_CTL, + PHASE_NOT_RESET, PHASE_NOT_RESET); + } + + err = rtsx_pci_send_cmd(pcr, 2000); + if (err < 0) + return err; + + /* Wait SSC clock stable */ + udelay(SSC_CLOCK_STABLE_WAIT); + err = rtsx_pci_write_register(pcr, CLK_CTL, CLK_LOW_FREQ, 0); + if (err < 0) + return err; + + pcr->cur_clock = clk; + return 0; + +} + +void rts5228_init_params(struct rtsx_pcr *pcr) +{ + struct rtsx_cr_option *option = &pcr->option; + struct rtsx_hw_param *hw_param = &pcr->hw_param; + + pcr->extra_caps = EXTRA_CAPS_SD_SDR50 | EXTRA_CAPS_SD_SDR104; + pcr->num_slots = 1; + pcr->ops = &rts5228_pcr_ops; + + pcr->flags = 0; + pcr->card_drive_sel = RTSX_CARD_DRIVE_DEFAULT; + pcr->sd30_drive_sel_1v8 = CFG_DRIVER_TYPE_B; + pcr->sd30_drive_sel_3v3 = CFG_DRIVER_TYPE_B; + pcr->aspm_en = ASPM_L1_EN; + pcr->tx_initial_phase = SET_CLOCK_PHASE(28, 27, 11); + pcr->rx_initial_phase = SET_CLOCK_PHASE(24, 6, 5); + + pcr->ic_version = rts5228_get_ic_version(pcr); + pcr->sd_pull_ctl_enable_tbl = rts5228_sd_pull_ctl_enable_tbl; + pcr->sd_pull_ctl_disable_tbl = rts5228_sd_pull_ctl_disable_tbl; + + pcr->reg_pm_ctrl3 = RTS5228_AUTOLOAD_CFG3; + + option->dev_flags = (LTR_L1SS_PWR_GATE_CHECK_CARD_EN + | LTR_L1SS_PWR_GATE_EN); + option->ltr_en = true; + + /* init latency of active, idle, L1OFF to 60us, 300us, 3ms */ + option->ltr_active_latency = LTR_ACTIVE_LATENCY_DEF; + option->ltr_idle_latency = LTR_IDLE_LATENCY_DEF; + option->ltr_l1off_latency = LTR_L1OFF_LATENCY_DEF; + option->l1_snooze_delay = L1_SNOOZE_DELAY_DEF; + option->ltr_l1off_sspwrgate = 0x7F; + option->ltr_l1off_snooze_sspwrgate = 0x78; + + option->ocp_en = 1; + hw_param->interrupt_en |= SD_OC_INT_EN; + hw_param->ocp_glitch = SD_OCP_GLITCH_800U; + option->sd_800mA_ocp_thd = RTS5228_LDO1_OCP_THD_930; +} diff --git a/drivers/misc/cardreader/rts5228.h b/drivers/misc/cardreader/rts5228.h new file mode 100644 index 000000000000..6a872246aeed --- /dev/null +++ b/drivers/misc/cardreader/rts5228.h @@ -0,0 +1,168 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Driver for Realtek PCI-Express card reader + * + * Copyright(c) 2018-2019 Realtek Semiconductor Corp. All rights reserved. + * + * Author: + * Ricky WU + * Rui FENG + * Wei WANG + */ +#ifndef RTS5228_H +#define RTS5228_H + + +#define RTS5228_AUTOLOAD_CFG0 0xFF7B +#define RTS5228_AUTOLOAD_CFG1 0xFF7C +#define RTS5228_AUTOLOAD_CFG2 0xFF7D +#define RTS5228_AUTOLOAD_CFG3 0xFF7E +#define RTS5228_AUTOLOAD_CFG4 0xFF7F + +#define RTS5228_REG_VREF 0xFE97 +#define RTS5228_PWD_SUSPND_EN (1 << 4) + +#define RTS5228_PAD_H3L1 0xFF79 +#define PAD_GPIO_H3L1 (1 << 3) + +/* SSC_CTL2 0xFC12 */ +#define RTS5228_SSC_DEPTH_MASK 0x07 +#define RTS5228_SSC_DEPTH_DISALBE 0x00 +#define RTS5228_SSC_DEPTH_8M 0x01 +#define RTS5228_SSC_DEPTH_4M 0x02 +#define RTS5228_SSC_DEPTH_2M 0x03 +#define RTS5228_SSC_DEPTH_1M 0x04 +#define RTS5228_SSC_DEPTH_512K 0x05 +#define RTS5228_SSC_DEPTH_256K 0x06 +#define RTS5228_SSC_DEPTH_128K 0x07 + +/* DMACTL 0xFE2C */ +#define RTS5228_DMA_PACK_SIZE_MASK 0xF0 + +#define RTS5228_REG_LDO12_CFG 0xFF6E +#define RTS5228_LDO12_VO_TUNE_MASK (0x07<<1) +#define RTS5228_LDO12_100 (0x00<<1) +#define RTS5228_LDO12_105 (0x01<<1) +#define RTS5228_LDO12_110 (0x02<<1) +#define RTS5228_LDO12_115 (0x03<<1) +#define RTS5228_LDO12_120 (0x04<<1) +#define RTS5228_LDO12_125 (0x05<<1) +#define RTS5228_LDO12_130 (0x06<<1) +#define RTS5228_LDO12_135 (0x07<<1) +#define RTS5228_REG_PWD_LDO12 (0x01<<0) + +#define RTS5228_REG_LDO12_L12 0xFF6F +#define RTS5228_LDO12_L12_MASK (0x07<<4) +#define RTS5228_LDO12_L12_120 (0x04<<4) + +/* LDO control register */ +#define RTS5228_CARD_PWR_CTL 0xFD50 +#define RTS5228_PUPDC (0x01<<5) + +#define RTS5228_LDO1233318_POW_CTL 0xFF70 +#define RTS5228_LDO3318_POWERON (0x01<<3) +#define RTS5228_LDO1_POWEROFF (0x00<<0) +#define RTS5228_LDO1_SOFTSTART (0x01<<0) +#define RTS5228_LDO1_FULLON (0x03<<0) +#define RTS5228_LDO1_POWERON_MASK (0x03<<0) +#define RTS5228_LDO_POWERON_MASK (0x0F<<0) + +#define RTS5228_DV3318_CFG 0xFF71 +#define RTS5228_DV3318_TUNE_MASK (0x07<<4) +#define RTS5228_DV3318_17 (0x00<<4) +#define RTS5228_DV3318_1V75 (0x01<<4) +#define RTS5228_DV3318_18 (0x02<<4) +#define RTS5228_DV3318_1V85 (0x03<<4) +#define RTS5228_DV3318_19 (0x04<<4) +#define RTS5228_DV3318_33 (0x07<<4) +#define RTS5228_DV3318_SR_MASK (0x03<<2) +#define RTS5228_DV3318_SR_0 (0x00<<2) +#define RTS5228_DV3318_SR_250 (0x01<<2) +#define RTS5228_DV3318_SR_500 (0x02<<2) +#define RTS5228_DV3318_SR_1000 (0x03<<2) + +#define RTS5228_LDO1_CFG0 0xFF72 +#define RTS5228_LDO1_OCP_THD_MASK (0x07<<5) +#define RTS5228_LDO1_OCP_EN (0x01<<4) +#define RTS5228_LDO1_OCP_LMT_THD_MASK (0x03<<2) +#define RTS5228_LDO1_OCP_LMT_EN (0x01<<1) + +#define RTS5228_LDO1_OCP_THD_730 (0x00<<5) +#define RTS5228_LDO1_OCP_THD_780 (0x01<<5) +#define RTS5228_LDO1_OCP_THD_860 (0x02<<5) +#define RTS5228_LDO1_OCP_THD_930 (0x03<<5) +#define RTS5228_LDO1_OCP_THD_1000 (0x04<<5) +#define RTS5228_LDO1_OCP_THD_1070 (0x05<<5) +#define RTS5228_LDO1_OCP_THD_1140 (0x06<<5) +#define RTS5228_LDO1_OCP_THD_1220 (0x07<<5) + +#define RTS5228_LDO1_LMT_THD_450 (0x00<<2) +#define RTS5228_LDO1_LMT_THD_1000 (0x01<<2) +#define RTS5228_LDO1_LMT_THD_1500 (0x02<<2) +#define RTS5228_LDO1_LMT_THD_2000 (0x03<<2) + +#define RTS5228_LDO1_CFG1 0xFF73 +#define RTS5228_LDO1_SR_TIME_MASK (0x03<<6) +#define RTS5228_LDO1_SR_0_0 (0x00<<6) +#define RTS5228_LDO1_SR_0_25 (0x01<<6) +#define RTS5228_LDO1_SR_0_5 (0x02<<6) +#define RTS5228_LDO1_SR_1_0 (0x03<<6) +#define RTS5228_LDO1_TUNE_MASK (0x07<<1) +#define RTS5228_LDO1_18 (0x05<<1) +#define RTS5228_LDO1_33 (0x07<<1) +#define RTS5228_LDO1_PWD_MASK (0x01<<0) + +#define RTS5228_AUXCLK_GAT_CTL 0xFF74 + +#define RTS5228_REG_RREF_CTL_0 0xFF75 +#define RTS5228_FORCE_RREF_EXTL (0x01<<7) +#define RTS5228_REG_BG33_MASK (0x07<<0) +#define RTS5228_RREF_12_1V (0x04<<0) +#define RTS5228_RREF_12_3V (0x05<<0) + +#define RTS5228_REG_RREF_CTL_1 0xFF76 + +#define RTS5228_REG_RREF_CTL_2 0xFF77 +#define RTS5228_TEST_INTL_RREF (0x01<<7) +#define RTS5228_DGLCH_TIME_MASK (0x03<<5) +#define RTS5228_DGLCH_TIME_50 (0x00<<5) +#define RTS5228_DGLCH_TIME_75 (0x01<<5) +#define RTS5228_DGLCH_TIME_100 (0x02<<5) +#define RTS5228_DGLCH_TIME_125 (0x03<<5) +#define RTS5228_REG_REXT_TUNE_MASK (0x1F<<0) + +#define RTS5228_REG_PME_FORCE_CTL 0xFF78 +#define FORCE_PM_CONTROL 0x20 +#define FORCE_PM_VALUE 0x10 + + +/* Single LUN, support SD */ +#define DEFAULT_SINGLE 0 +#define SD_LUN 1 + + +/* For Change_FPGA_SSCClock Function */ +#define MULTIPLY_BY_1 0x00 +#define MULTIPLY_BY_2 0x01 +#define MULTIPLY_BY_3 0x02 +#define MULTIPLY_BY_4 0x03 +#define MULTIPLY_BY_5 0x04 +#define MULTIPLY_BY_6 0x05 +#define MULTIPLY_BY_7 0x06 +#define MULTIPLY_BY_8 0x07 +#define MULTIPLY_BY_9 0x08 +#define MULTIPLY_BY_10 0x09 + +#define DIVIDE_BY_2 0x01 +#define DIVIDE_BY_3 0x02 +#define DIVIDE_BY_4 0x03 +#define DIVIDE_BY_5 0x04 +#define DIVIDE_BY_6 0x05 +#define DIVIDE_BY_7 0x06 +#define DIVIDE_BY_8 0x07 +#define DIVIDE_BY_9 0x08 +#define DIVIDE_BY_10 0x09 + +int rts5228_pci_switch_clock(struct rtsx_pcr *pcr, unsigned int card_clock, + u8 ssc_depth, bool initial_mode, bool double_clk, bool vpclk); + +#endif /* RTS5228_H */ diff --git a/drivers/misc/cardreader/rtsx_pcr.c b/drivers/misc/cardreader/rtsx_pcr.c index 0d5928bc1b6d..5ff690d2e9a9 100644 --- a/drivers/misc/cardreader/rtsx_pcr.c +++ b/drivers/misc/cardreader/rtsx_pcr.c @@ -23,6 +23,7 @@ #include "rtsx_pcr.h" #include "rts5261.h" +#include "rts5228.h" static bool msi_en = true; module_param(msi_en, bool, S_IRUGO | S_IWUSR); @@ -50,6 +51,7 @@ static const struct pci_device_id rtsx_pci_ids[] = { { PCI_DEVICE(0x10EC, 0x525A), PCI_CLASS_OTHERS << 16, 0xFF0000 }, { PCI_DEVICE(0x10EC, 0x5260), PCI_CLASS_OTHERS << 16, 0xFF0000 }, { PCI_DEVICE(0x10EC, 0x5261), PCI_CLASS_OTHERS << 16, 0xFF0000 }, + { PCI_DEVICE(0x10EC, 0x5228), PCI_CLASS_OTHERS << 16, 0xFF0000 }, { 0, } }; @@ -206,16 +208,10 @@ int __rtsx_pci_write_phy_register(struct rtsx_pcr *pcr, u8 addr, u16 val) int err, i, finished = 0; u8 tmp; - rtsx_pci_init_cmd(pcr); - - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PHYDATA0, 0xFF, (u8)val); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PHYDATA1, 0xFF, (u8)(val >> 8)); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PHYADDR, 0xFF, addr); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PHYRWCTL, 0xFF, 0x81); - - err = rtsx_pci_send_cmd(pcr, 100); - if (err < 0) - return err; + rtsx_pci_write_register(pcr, PHYDATA0, 0xFF, (u8)val); + rtsx_pci_write_register(pcr, PHYDATA1, 0xFF, (u8)(val >> 8)); + rtsx_pci_write_register(pcr, PHYADDR, 0xFF, addr); + rtsx_pci_write_register(pcr, PHYRWCTL, 0xFF, 0x81); for (i = 0; i < 100000; i++) { err = rtsx_pci_read_register(pcr, PHYRWCTL, &tmp); @@ -247,16 +243,10 @@ int __rtsx_pci_read_phy_register(struct rtsx_pcr *pcr, u8 addr, u16 *val) { int err, i, finished = 0; u16 data; - u8 *ptr, tmp; - - rtsx_pci_init_cmd(pcr); + u8 tmp, val1, val2; - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PHYADDR, 0xFF, addr); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PHYRWCTL, 0xFF, 0x80); - - err = rtsx_pci_send_cmd(pcr, 100); - if (err < 0) - return err; + rtsx_pci_write_register(pcr, PHYADDR, 0xFF, addr); + rtsx_pci_write_register(pcr, PHYRWCTL, 0xFF, 0x80); for (i = 0; i < 100000; i++) { err = rtsx_pci_read_register(pcr, PHYRWCTL, &tmp); @@ -272,17 +262,9 @@ int __rtsx_pci_read_phy_register(struct rtsx_pcr *pcr, u8 addr, u16 *val) if (!finished) return -ETIMEDOUT; - rtsx_pci_init_cmd(pcr); - - rtsx_pci_add_cmd(pcr, READ_REG_CMD, PHYDATA0, 0, 0); - rtsx_pci_add_cmd(pcr, READ_REG_CMD, PHYDATA1, 0, 0); - - err = rtsx_pci_send_cmd(pcr, 100); - if (err < 0) - return err; - - ptr = rtsx_pci_get_cmd_data(pcr); - data = ((u16)ptr[1] << 8) | ptr[0]; + rtsx_pci_read_register(pcr, PHYDATA0, &val1); + rtsx_pci_read_register(pcr, PHYDATA1, &val2); + data = val1 | (val2 << 8); if (val) *val = data; @@ -417,7 +399,7 @@ static void rtsx_pci_add_sg_tbl(struct rtsx_pcr *pcr, if (end) option |= RTSX_SG_END; - if (PCI_PID(pcr) == PID_5261) { + if ((PCI_PID(pcr) == PID_5261) || (PCI_PID(pcr) == PID_5228)) { if (len > 0xFFFF) val = ((u64)addr << 32) | (((u64)len & 0xFFFF) << 16) | (((u64)len >> 16) << 6) | option; @@ -723,6 +705,9 @@ int rtsx_pci_switch_clock(struct rtsx_pcr *pcr, unsigned int card_clock, if (PCI_PID(pcr) == PID_5261) return rts5261_pci_switch_clock(pcr, card_clock, ssc_depth, initial_mode, double_clk, vpclk); + if (PCI_PID(pcr) == PID_5228) + return rts5228_pci_switch_clock(pcr, card_clock, + ssc_depth, initial_mode, double_clk, vpclk); if (initial_mode) { /* We use 250k(around) here, in initial stage */ @@ -1202,6 +1187,36 @@ void rtsx_pci_clear_ocpstat(struct rtsx_pcr *pcr) } } +void rtsx_pci_enable_oobs_polling(struct rtsx_pcr *pcr) +{ + u16 val; + + if ((PCI_PID(pcr) != PID_525A) && (PCI_PID(pcr) != PID_5260)) { + rtsx_pci_read_phy_register(pcr, 0x01, &val); + val |= 1<<9; + rtsx_pci_write_phy_register(pcr, 0x01, val); + } + rtsx_pci_write_register(pcr, REG_CFG_OOBS_OFF_TIMER, 0xFF, 0x32); + rtsx_pci_write_register(pcr, REG_CFG_OOBS_ON_TIMER, 0xFF, 0x05); + rtsx_pci_write_register(pcr, REG_CFG_VCM_ON_TIMER, 0xFF, 0x83); + rtsx_pci_write_register(pcr, REG_CFG_OOBS_POLLING, 0xFF, 0xDE); + +} + +void rtsx_pci_disable_oobs_polling(struct rtsx_pcr *pcr) +{ + u16 val; + + if ((PCI_PID(pcr) != PID_525A) && (PCI_PID(pcr) != PID_5260)) { + rtsx_pci_read_phy_register(pcr, 0x01, &val); + val &= ~(1<<9); + rtsx_pci_write_phy_register(pcr, 0x01, val); + } + rtsx_pci_write_register(pcr, REG_CFG_VCM_ON_TIMER, 0xFF, 0x03); + rtsx_pci_write_register(pcr, REG_CFG_OOBS_POLLING, 0xFF, 0x00); + +} + int rtsx_sd_power_off_card3v3(struct rtsx_pcr *pcr) { rtsx_pci_write_register(pcr, CARD_CLK_EN, SD_CLK_EN | @@ -1233,6 +1248,10 @@ static int rtsx_pci_init_hw(struct rtsx_pcr *pcr) { int err; + if (PCI_PID(pcr) == PID_5228) + rtsx_pci_write_register(pcr, RTS5228_LDO1_CFG1, RTS5228_LDO1_SR_TIME_MASK, + RTS5228_LDO1_SR_0_5); + pcr->pcie_cap = pci_find_capability(pcr->pci, PCI_CAP_ID_EXP); rtsx_pci_writel(pcr, RTSX_HCBAR, pcr->host_cmds_addr); @@ -1280,6 +1299,9 @@ static int rtsx_pci_init_hw(struct rtsx_pcr *pcr) if (PCI_PID(pcr) == PID_5261) rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL2, 0xFF, RTS5261_SSC_DEPTH_2M); + else if (PCI_PID(pcr) == PID_5228) + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL2, 0xFF, + RTS5228_SSC_DEPTH_2M); else rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL2, 0xFF, 0x12); @@ -1314,6 +1336,7 @@ static int rtsx_pci_init_hw(struct rtsx_pcr *pcr) case PID_525A: case PID_5260: case PID_5261: + case PID_5228: rtsx_pci_write_register(pcr, PM_CLK_FORCE_CTL, 1, 1); break; default: @@ -1401,6 +1424,10 @@ static int rtsx_pci_init_chip(struct rtsx_pcr *pcr) case 0x5261: rts5261_init_params(pcr); break; + + case 0x5228: + rts5228_init_params(pcr); + break; } pcr_dbg(pcr, "PID: 0x%04x, IC version: 0x%02x\n", diff --git a/drivers/misc/cardreader/rtsx_pcr.h b/drivers/misc/cardreader/rtsx_pcr.h index 024cbd998b2a..6b322db8738e 100644 --- a/drivers/misc/cardreader/rtsx_pcr.h +++ b/drivers/misc/cardreader/rtsx_pcr.h @@ -53,6 +53,7 @@ void rts525a_init_params(struct rtsx_pcr *pcr); void rtl8411b_init_params(struct rtsx_pcr *pcr); void rts5260_init_params(struct rtsx_pcr *pcr); void rts5261_init_params(struct rtsx_pcr *pcr); +void rts5228_init_params(struct rtsx_pcr *pcr); static inline u8 map_sd_drive(int idx) { @@ -70,6 +71,8 @@ static inline u8 map_sd_drive(int idx) #define rts5209_vendor_setting1_valid(reg) (!((reg) & 0x80)) #define rts5209_vendor_setting2_valid(reg) ((reg) & 0x80) +#define rtsx_check_mmc_support(reg) ((reg) & 0x10) +#define rtsx_reg_to_rtd3(reg) ((reg) & 0x02) #define rtsx_reg_to_aspm(reg) (((reg) >> 28) & 0x03) #define rtsx_reg_to_sd30_drive_sel_1v8(reg) (((reg) >> 26) & 0x03) #define rtsx_reg_to_sd30_drive_sel_3v3(reg) (((reg) >> 5) & 0x03) @@ -100,6 +103,8 @@ void rtsx_pci_disable_ocp(struct rtsx_pcr *pcr); void rtsx_pci_enable_ocp(struct rtsx_pcr *pcr); int rtsx_pci_get_ocpstat(struct rtsx_pcr *pcr, u8 *val); void rtsx_pci_clear_ocpstat(struct rtsx_pcr *pcr); +void rtsx_pci_enable_oobs_polling(struct rtsx_pcr *pcr); +void rtsx_pci_disable_oobs_polling(struct rtsx_pcr *pcr); int rtsx_sd_power_off_card3v3(struct rtsx_pcr *pcr); int rtsx_ms_power_off_card3v3(struct rtsx_pcr *pcr); diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c index 5a71f6678fd3..bce910de8b4d 100644 --- a/drivers/mmc/host/rtsx_pci_sdmmc.c +++ b/drivers/mmc/host/rtsx_pci_sdmmc.c @@ -1336,6 +1336,8 @@ static void init_extra_caps(struct realtek_pci_sdmmc *host) mmc->caps |= MMC_CAP_1_8V_DDR; if (pcr->extra_caps & EXTRA_CAPS_MMC_8BIT) mmc->caps |= MMC_CAP_8_BIT_DATA; + if (pcr->extra_caps & EXTRA_CAPS_NO_MMC) + mmc->caps2 |= MMC_CAP2_NO_MMC; } static void realtek_init_host(struct realtek_pci_sdmmc *host) diff --git a/include/linux/rtsx_pci.h b/include/linux/rtsx_pci.h index e8780d4e4636..27a6ea82aeea 100644 --- a/include/linux/rtsx_pci.h +++ b/include/linux/rtsx_pci.h @@ -305,6 +305,8 @@ #define SD30_CLK_STOP_CFG0 0x01 #define REG_PRE_RW_MODE 0xFD70 #define EN_INFINITE_MODE 0x01 +#define REG_CRC_DUMMY_0 0xFD71 +#define CFG_SD_POW_AUTO_PD (1<<0) #define SRCTL 0xFC13 @@ -599,6 +601,7 @@ #define ASPM_FORCE_CTL 0xFE57 #define FORCE_ASPM_CTL0 0x10 +#define FORCE_ASPM_CTL1 0x20 #define FORCE_ASPM_VAL_MASK 0x03 #define FORCE_ASPM_L1_EN 0x02 #define FORCE_ASPM_L0_EN 0x01 @@ -667,6 +670,11 @@ #define PM_WAKE_EN 0x01 #define PM_CTRL4 0xFF47 +#define REG_CFG_OOBS_OFF_TIMER 0xFEA6 +#define REG_CFG_OOBS_ON_TIMER 0xFEA7 +#define REG_CFG_VCM_ON_TIMER 0xFEA8 +#define REG_CFG_OOBS_POLLING 0xFEA9 + /* Memory mapping */ #define SRAM_BASE 0xE600 #define RBUF_BASE 0xF400 @@ -1204,6 +1212,7 @@ struct rtsx_pcr { #define EXTRA_CAPS_MMC_HSDDR (1 << 3) #define EXTRA_CAPS_MMC_HS200 (1 << 4) #define EXTRA_CAPS_MMC_8BIT (1 << 5) +#define EXTRA_CAPS_NO_MMC (1 << 7) u32 extra_caps; #define IC_VER_A 0 @@ -1242,6 +1251,7 @@ struct rtsx_pcr { u8 dma_error_count; u8 ocp_stat; u8 ocp_stat2; + u8 rtd3_en; }; #define PID_524A 0x524A @@ -1250,6 +1260,7 @@ struct rtsx_pcr { #define PID_525A 0x525A #define PID_5260 0x5260 #define PID_5261 0x5261 +#define PID_5228 0x5228 #define CHK_PCI_PID(pcr, pid) ((pcr)->pci->device == (pid)) #define PCI_VID(pcr) ((pcr)->pci->vendor) -- cgit v1.2.3-59-g8ed1b From eacfbf589c904bf8362cbd2d6cac123b0230e272 Mon Sep 17 00:00:00 2001 From: Xu Yilun Date: Mon, 13 Jul 2020 09:47:46 +0800 Subject: fpga: dfl: pci: add device id for Intel FPGA PAC N3000 Add PCIe Device ID for Intel FPGA PAC N3000. Signed-off-by: Wu Hao Signed-off-by: Xu Yilun Signed-off-by: Matthew Gerlach Signed-off-by: Russ Weight Reviewed-by: Wu Hao Reviewed-by: Tom Rix Signed-off-by: Moritz Fischer --- drivers/fpga/dfl-pci.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/fpga/dfl-pci.c b/drivers/fpga/dfl-pci.c index 4a14a24b505e..3408bb8f1c65 100644 --- a/drivers/fpga/dfl-pci.c +++ b/drivers/fpga/dfl-pci.c @@ -64,6 +64,7 @@ static void cci_pci_free_irq(struct pci_dev *pcidev) #define PCIE_DEVICE_ID_PF_INT_5_X 0xBCBD #define PCIE_DEVICE_ID_PF_INT_6_X 0xBCC0 #define PCIE_DEVICE_ID_PF_DSC_1_X 0x09C4 +#define PCIE_DEVICE_ID_INTEL_PAC_N3000 0x0B30 /* VF Device */ #define PCIE_DEVICE_ID_VF_INT_5_X 0xBCBF #define PCIE_DEVICE_ID_VF_INT_6_X 0xBCC1 @@ -76,6 +77,7 @@ static struct pci_device_id cci_pcie_id_tbl[] = { {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCIE_DEVICE_ID_VF_INT_6_X),}, {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCIE_DEVICE_ID_PF_DSC_1_X),}, {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCIE_DEVICE_ID_VF_DSC_1_X),}, + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCIE_DEVICE_ID_INTEL_PAC_N3000),}, {0,} }; MODULE_DEVICE_TABLE(pci, cci_pcie_id_tbl); -- cgit v1.2.3-59-g8ed1b From 270ff6048f45759d31a7b62d3983b084153837f5 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Fri, 3 Jul 2020 14:46:00 +0800 Subject: phy: cadence: salvo: fix wrong bit definition It fixes RX detect wakeup using USB3 device, otherwise, the USB3 device can't wakeup USB PHY when the PHY is in 32Khz clock. Fixes: 50d35aa8c15f ("phy: cadence: salvo: add salvo phy driver") Signed-off-by: Peter Chen Link: https://lore.kernel.org/r/20200703064600.14181-1-peter.chen@nxp.com Signed-off-by: Vinod Koul --- drivers/phy/cadence/phy-cadence-salvo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/cadence/phy-cadence-salvo.c b/drivers/phy/cadence/phy-cadence-salvo.c index 1ecbb964cd21..016514e4aa54 100644 --- a/drivers/phy/cadence/phy-cadence-salvo.c +++ b/drivers/phy/cadence/phy-cadence-salvo.c @@ -88,7 +88,7 @@ #define TB_ADDR_TX_RCVDETSC_CTRL 0x4124 /* TB_ADDR_TX_RCVDETSC_CTRL */ -#define RXDET_IN_P3_32KHZ BIT(1) +#define RXDET_IN_P3_32KHZ BIT(0) struct cdns_reg_pairs { u16 val; -- cgit v1.2.3-59-g8ed1b From 010f0dff9f07fe4e4633807253b02126cf8981fa Mon Sep 17 00:00:00 2001 From: Tiezhu Yang Date: Tue, 7 Jul 2020 17:22:26 +0800 Subject: phy: allwinner: Make PHY_SUN6I_MIPI_DPHY depend on COMMON_CLK When CONFIG_ARCH_SUNXI is not set but CONFIG_COMPILE_TEST=y, CONFIG_HAVE_CLK=y, CONFIG_HAVE_LEGACY_CLK=y, there exists the following build errors with CONFIG_PHY_SUN6I_MIPI_DPHY=y: drivers/phy/allwinner/phy-sun6i-mipi-dphy.o: In function `sun6i_dphy_init': phy-sun6i-mipi-dphy.c:(.text+0x320): undefined reference to `clk_set_rate_exclusive' drivers/phy/allwinner/phy-sun6i-mipi-dphy.o: In function `sun6i_dphy_exit': phy-sun6i-mipi-dphy.c:(.text+0x2c8): undefined reference to `clk_rate_exclusive_put' clk_set_rate_exclusive() and clk_rate_exclusive_put() are defined in drivers/clk/clk.c, this file is built under CONFIG_COMMON_CLK, so in order to build drivers/phy/allwinner/phy-sun6i-mipi-dphy.c successful used with various configs, CONFIG_PHY_SUN6I_MIPI_DPHY should depend on CONFIG_COMMON_CLK. Fixes: 133552bf03ed ("phy: Remove CONFIG_ARCH_* check for related subdir in Makefile") Reported-by: kernel test robot Signed-off-by: Tiezhu Yang Link: https://lore.kernel.org/r/1594113746-25393-1-git-send-email-yangtiezhu@loongson.cn Signed-off-by: Vinod Koul --- drivers/phy/allwinner/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/allwinner/Kconfig b/drivers/phy/allwinner/Kconfig index e760d89d3976..fb584518b2d0 100644 --- a/drivers/phy/allwinner/Kconfig +++ b/drivers/phy/allwinner/Kconfig @@ -22,7 +22,7 @@ config PHY_SUN4I_USB config PHY_SUN6I_MIPI_DPHY tristate "Allwinner A31 MIPI D-PHY Support" depends on ARCH_SUNXI || COMPILE_TEST - depends on HAS_IOMEM + depends on HAS_IOMEM && COMMON_CLK depends on RESET_CONTROLLER select GENERIC_PHY select GENERIC_PHY_MIPI_DPHY -- cgit v1.2.3-59-g8ed1b From 03815930c162561a5c204494b4160d6ccf631b0b Mon Sep 17 00:00:00 2001 From: Kunihiko Hayashi Date: Wed, 8 Jul 2020 17:57:44 +0900 Subject: dt-bindings: phy: uniphier: Fix incorrect clocks and clock-names for PXs3 usb3-hsphy The usb3-hsphy for PXs3 SoC needs to accept 3 clocks like usb3-ssphy. Fixes: 134ab2845acb ("dt-bindings: phy: Convert UniPhier USB3-PHY conroller to json-schema") Signed-off-by: Kunihiko Hayashi Link: https://lore.kernel.org/r/1594198664-29381-1-git-send-email-hayashi.kunihiko@socionext.com Signed-off-by: Vinod Koul --- .../devicetree/bindings/phy/socionext,uniphier-usb3hs-phy.yaml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/phy/socionext,uniphier-usb3hs-phy.yaml b/Documentation/devicetree/bindings/phy/socionext,uniphier-usb3hs-phy.yaml index f88d36207b87..c871d462c952 100644 --- a/Documentation/devicetree/bindings/phy/socionext,uniphier-usb3hs-phy.yaml +++ b/Documentation/devicetree/bindings/phy/socionext,uniphier-usb3hs-phy.yaml @@ -31,12 +31,16 @@ properties: clocks: minItems: 1 - maxItems: 2 + maxItems: 3 clock-names: oneOf: - const: link # for PXs2 - - items: # for PXs3 + - items: # for PXs3 with phy-ext + - const: link + - const: phy + - const: phy-ext + - items: # for others - const: link - const: phy -- cgit v1.2.3-59-g8ed1b From dcbabfeb17c3c2fdb6bc92a3031ecd37df1834a8 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Wed, 8 Jul 2020 15:38:00 +0200 Subject: phy: exynos5-usbdrd: Calibrating makes sense only for USB2.0 PHY PHY calibration is needed only for USB2.0 (UTMI) PHY, so skip calling calibration code when phy_calibrate() is called for USB3.0 (PIPE3) PHY. Fixes: d8c80bb3b55b ("phy: exynos5-usbdrd: Calibrate LOS levels for exynos5420/5800") Signed-off-by: Marek Szyprowski Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20200708133800.3336-1-m.szyprowski@samsung.com Signed-off-by: Vinod Koul --- drivers/phy/samsung/phy-exynos5-usbdrd.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/phy/samsung/phy-exynos5-usbdrd.c b/drivers/phy/samsung/phy-exynos5-usbdrd.c index eb06ce9f748f..9930d2027e94 100644 --- a/drivers/phy/samsung/phy-exynos5-usbdrd.c +++ b/drivers/phy/samsung/phy-exynos5-usbdrd.c @@ -714,7 +714,9 @@ static int exynos5_usbdrd_phy_calibrate(struct phy *phy) struct phy_usb_instance *inst = phy_get_drvdata(phy); struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); - return exynos5420_usbdrd_phy_calibrate(phy_drd); + if (inst->phy_cfg->id == EXYNOS5_DRDPHY_UTMI) + return exynos5420_usbdrd_phy_calibrate(phy_drd); + return 0; } static const struct phy_ops exynos5_usbdrd_phy_ops = { -- cgit v1.2.3-59-g8ed1b From 901e0a8f14a1497ce6229445daac4aec1dd93b92 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Wed, 8 Jul 2020 18:58:04 +0530 Subject: phy-mvebu-a3700-utmi: correct typo in struct mvebu_a3700_utmi comments We get warning with W=1 build: drivers/phy/marvell/phy-mvebu-a3700-utmi.c:84: warning: Function parameter or member 'usb_misc' not described in 'mvebu_a3700_utmi' Fix that by correcting the typo usb_mis Link: https://lore.kernel.org/r/20200708132809.265967-1-vkoul@kernel.org Signed-off-by: Vinod Koul --- drivers/phy/marvell/phy-mvebu-a3700-utmi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/marvell/phy-mvebu-a3700-utmi.c b/drivers/phy/marvell/phy-mvebu-a3700-utmi.c index 23bc3bf5c4c0..8834436bc9db 100644 --- a/drivers/phy/marvell/phy-mvebu-a3700-utmi.c +++ b/drivers/phy/marvell/phy-mvebu-a3700-utmi.c @@ -72,7 +72,7 @@ struct mvebu_a3700_utmi_caps { * struct mvebu_a3700_utmi - PHY driver data * * @regs: PHY registers - * @usb_mis: Regmap with USB miscellaneous registers including PHY ones + * @usb_misc: Regmap with USB miscellaneous registers including PHY ones * @caps: PHY capabilities * @phy: PHY handle */ -- cgit v1.2.3-59-g8ed1b From c988b7c5fc056d3bec4cb337a57fc2fe6a04081a Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Wed, 8 Jul 2020 18:58:05 +0530 Subject: phy: mapphone-mdm6600: Add missing description for some structure fields We get warning with W=1 build: drivers/phy/motorola/phy-mapphone-mdm6600.c:185: warning: Function parameter or member 'val' not described in 'phy_mdm6600_cmd' drivers/phy/motorola/phy-mapphone-mdm6600.c:200: warning: Function parameter or member 'work' not described in 'phy_mdm6600_status' Fix that by adding description for 'val' and 'work' Link: https://lore.kernel.org/r/20200708132809.265967-2-vkoul@kernel.org Signed-off-by: Vinod Koul --- drivers/phy/motorola/phy-mapphone-mdm6600.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/phy/motorola/phy-mapphone-mdm6600.c b/drivers/phy/motorola/phy-mapphone-mdm6600.c index 94a34cf75eb3..5172971f4c36 100644 --- a/drivers/phy/motorola/phy-mapphone-mdm6600.c +++ b/drivers/phy/motorola/phy-mapphone-mdm6600.c @@ -178,6 +178,7 @@ static const struct phy_ops gpio_usb_ops = { /** * phy_mdm6600_cmd() - send a command request to mdm6600 * @ddata: device driver data + * @val: value of cmd to be set * * Configures the three command request GPIOs to the specified value. */ @@ -194,7 +195,7 @@ static void phy_mdm6600_cmd(struct phy_mdm6600 *ddata, int val) /** * phy_mdm6600_status() - read mdm6600 status lines - * @ddata: device driver data + * @work: work structure */ static void phy_mdm6600_status(struct work_struct *work) { -- cgit v1.2.3-59-g8ed1b From f0c47c74583cc55c7ff9bdafff63bfb33edbb940 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Wed, 8 Jul 2020 18:58:06 +0530 Subject: phy: xgene: remove unsigned integer comparison with less than zero MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We get warning with W=1 build: drivers/phy/phy-xgene.c: In function ‘xgene_phy_xlate’: drivers/phy/phy-xgene.c:1618:20: warning: comparison of unsigned expression in ‘< 0’ is always false [-Wtype-limits] 1618 | if (args->args[0] < MODE_SATA || args->args[0] >= MODE_MAX) | args is uint32_t so can never be less than zero, so remove this check Link: https://lore.kernel.org/r/20200708132809.265967-3-vkoul@kernel.org Signed-off-by: Vinod Koul --- drivers/phy/phy-xgene.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/phy-xgene.c b/drivers/phy/phy-xgene.c index 7a33ec12f71b..b88922e7de1d 100644 --- a/drivers/phy/phy-xgene.c +++ b/drivers/phy/phy-xgene.c @@ -1615,7 +1615,7 @@ static struct phy *xgene_phy_xlate(struct device *dev, if (args->args_count <= 0) return ERR_PTR(-EINVAL); - if (args->args[0] < MODE_SATA || args->args[0] >= MODE_MAX) + if (args->args[0] >= MODE_MAX) return ERR_PTR(-EINVAL); ctx->mode = args->args[0]; -- cgit v1.2.3-59-g8ed1b From 72fbf95f36218ec2a901e0eb7c3aa0bea6f1f396 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Wed, 8 Jul 2020 18:58:07 +0530 Subject: phy: rockchip-typec: use correct format for structure description We get warning with W=1 build: drivers/phy/rockchip/phy-rockchip-typec.c:360: warning: cannot understand function prototype: 'struct rockchip_usb3phy_port_cfg ' The 'struct rockchip_usb3phy_port_cfg ' is commented properly but uses wrong format, so fix that up Link: https://lore.kernel.org/r/20200708132809.265967-4-vkoul@kernel.org Signed-off-by: Vinod Koul --- drivers/phy/rockchip/phy-rockchip-typec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/rockchip/phy-rockchip-typec.c b/drivers/phy/rockchip/phy-rockchip-typec.c index 24563160197f..70a31251b202 100644 --- a/drivers/phy/rockchip/phy-rockchip-typec.c +++ b/drivers/phy/rockchip/phy-rockchip-typec.c @@ -347,7 +347,7 @@ struct usb3phy_reg { }; /** - * struct rockchip_usb3phy_port_cfg: usb3-phy port configuration. + * struct rockchip_usb3phy_port_cfg - usb3-phy port configuration. * @reg: the base address for usb3-phy config. * @typec_conn_dir: the register of type-c connector direction. * @usb3tousb2_en: the register of type-c force usb2 to usb2 enable. -- cgit v1.2.3-59-g8ed1b From 0672fb0d422246725c3af0957cbdbedcc86008a1 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Wed, 8 Jul 2020 18:58:08 +0530 Subject: phy: exynos5-usbdrd: use correct format for structure description We get warning with W=1 build: drivers/phy/samsung/phy-exynos5-usbdrd.c:211: warning: Function parameter or member 'phys' not described in 'exynos5_usbdrd_phy' drivers/phy/samsung/phy-exynos5-usbdrd.c:211: warning: Function parameter or member 'vbus' not described in 'exynos5_usbdrd_phy' drivers/phy/samsung/phy-exynos5-usbdrd.c:211: warning: Function parameter or member 'vbus_boost' not described in 'exynos5_usbdrd_phy' These members are provided with description but format is not quite right resulting in above warnings Acked-by: Marek Szyprowski Link: https://lore.kernel.org/r/20200708132809.265967-5-vkoul@kernel.org Signed-off-by: Vinod Koul --- drivers/phy/samsung/phy-exynos5-usbdrd.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/phy/samsung/phy-exynos5-usbdrd.c b/drivers/phy/samsung/phy-exynos5-usbdrd.c index 9930d2027e94..0d818b77a0d8 100644 --- a/drivers/phy/samsung/phy-exynos5-usbdrd.c +++ b/drivers/phy/samsung/phy-exynos5-usbdrd.c @@ -180,14 +180,14 @@ struct exynos5_usbdrd_phy_drvdata { * @utmiclk: clock for utmi+ phy * @itpclk: clock for ITP generation * @drv_data: pointer to SoC level driver data structure - * @phys[]: array for 'EXYNOS5_DRDPHYS_NUM' number of PHY + * @phys: array for 'EXYNOS5_DRDPHYS_NUM' number of PHY * instances each with its 'phy' and 'phy_cfg'. * @extrefclk: frequency select settings when using 'separate * reference clocks' for SS and HS operations * @ref_clk: reference clock to PHY block from which PHY's * operational clocks are derived - * vbus: VBUS regulator for phy - * vbus_boost: Boost regulator for VBUS present on few Exynos boards + * @vbus: VBUS regulator for phy + * @vbus_boost: Boost regulator for VBUS present on few Exynos boards */ struct exynos5_usbdrd_phy { struct device *dev; -- cgit v1.2.3-59-g8ed1b From 0ff35966d171ec99b118df666c1687cc86ba8d7e Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Wed, 8 Jul 2020 18:58:09 +0530 Subject: phy: stm32: use NULL instead of zero devm_clk_get() and devm_reset_control_get() expect a const char *id for the last arg, but a value of zero was provided. This results in below sparse warning: drivers/phy/st/phy-stm32-usbphyc.c:330:42: warning: Using plain integer as NULL pointer drivers/phy/st/phy-stm32-usbphyc.c:343:52: warning: Using plain integer as NULL pointer Instead of zero, use NULL Reviewed-by: Amelie Delaunay Link: https://lore.kernel.org/r/20200708132809.265967-6-vkoul@kernel.org Signed-off-by: Vinod Koul --- drivers/phy/st/phy-stm32-usbphyc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/phy/st/phy-stm32-usbphyc.c b/drivers/phy/st/phy-stm32-usbphyc.c index 56bdea4b0bd9..2b3639cba51a 100644 --- a/drivers/phy/st/phy-stm32-usbphyc.c +++ b/drivers/phy/st/phy-stm32-usbphyc.c @@ -327,7 +327,7 @@ static int stm32_usbphyc_probe(struct platform_device *pdev) if (IS_ERR(usbphyc->base)) return PTR_ERR(usbphyc->base); - usbphyc->clk = devm_clk_get(dev, 0); + usbphyc->clk = devm_clk_get(dev, NULL); if (IS_ERR(usbphyc->clk)) { ret = PTR_ERR(usbphyc->clk); dev_err(dev, "clk get failed: %d\n", ret); @@ -340,7 +340,7 @@ static int stm32_usbphyc_probe(struct platform_device *pdev) return ret; } - usbphyc->rst = devm_reset_control_get(dev, 0); + usbphyc->rst = devm_reset_control_get(dev, NULL); if (!IS_ERR(usbphyc->rst)) { reset_control_assert(usbphyc->rst); udelay(2); -- cgit v1.2.3-59-g8ed1b From 3a12c2b5f38227a7104ce9f2be682059c7c93100 Mon Sep 17 00:00:00 2001 From: "Saheed O. Bolarinwa" Date: Mon, 13 Jul 2020 14:22:25 +0200 Subject: cxl: Change PCIBIOS_SUCCESSFUL to 0 In reference to the PCI spec (Chapter 2), PCIBIOS* is an x86 concept. There scope should be limited within arch/x86. Change all PCIBIOS_SUCCESSFUL to 0 Signed-off-by: "Saheed O. Bolarinwa" Link: https://lore.kernel.org/r/20200713122247.10985-14-refactormyself@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/cxl/vphb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/misc/cxl/vphb.c b/drivers/misc/cxl/vphb.c index 1cf320e2a415..1264253cc07b 100644 --- a/drivers/misc/cxl/vphb.c +++ b/drivers/misc/cxl/vphb.c @@ -150,7 +150,7 @@ static int cxl_pcie_read_config(struct pci_bus *bus, unsigned int devfn, out: cxl_afu_configured_put(afu); - return rc ? PCIBIOS_DEVICE_NOT_FOUND : PCIBIOS_SUCCESSFUL; + return rc ? PCIBIOS_DEVICE_NOT_FOUND : 0; } static int cxl_pcie_write_config(struct pci_bus *bus, unsigned int devfn, @@ -184,7 +184,7 @@ static int cxl_pcie_write_config(struct pci_bus *bus, unsigned int devfn, out: cxl_afu_configured_put(afu); - return rc ? PCIBIOS_SET_FAILED : PCIBIOS_SUCCESSFUL; + return rc ? PCIBIOS_SET_FAILED : 0; } static struct pci_ops cxl_pcie_pci_ops = -- cgit v1.2.3-59-g8ed1b From fadbfc38dde26d31e901c3c85cf01332cb6a2224 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Tue, 14 Jul 2020 10:44:49 -0500 Subject: hpilo: Replace one-element 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]. For this particular case, it is important to notice that the cachelines change from 7 to 6 after the flexible-array conversion: $ pahole -C 'fifo' drivers/misc/hpilo.o struct fifo { u64 nrents; /* 0 8 */ u64 imask; /* 8 8 */ u64 merge; /* 16 8 */ u64 reset; /* 24 8 */ u8 pad_0[96]; /* 32 96 */ /* --- cacheline 2 boundary (128 bytes) --- */ u64 head; /* 128 8 */ u8 pad_1[120]; /* 136 120 */ /* --- cacheline 4 boundary (256 bytes) --- */ u64 tail; /* 256 8 */ u8 pad_2[120]; /* 264 120 */ /* --- cacheline 6 boundary (384 bytes) --- */ u64 fifobar[1]; /* 384 8 */ /* size: 392, cachelines: 7, members: 10 */ /* last cacheline: 8 bytes */ }; $ pahole -C 'fifo' drivers/misc/hpilo.o struct fifo { u64 nrents; /* 0 8 */ u64 imask; /* 8 8 */ u64 merge; /* 16 8 */ u64 reset; /* 24 8 */ u8 pad_0[96]; /* 32 96 */ /* --- cacheline 2 boundary (128 bytes) --- */ u64 head; /* 128 8 */ u8 pad_1[120]; /* 136 120 */ /* --- cacheline 4 boundary (256 bytes) --- */ u64 tail; /* 256 8 */ u8 pad_2[120]; /* 264 120 */ /* --- cacheline 6 boundary (384 bytes) --- */ u64 fifobar[]; /* 384 0 */ /* size: 384, cachelines: 6, members: 10 */ }; Lastly, remove unnecessary parentheses in fifo_sz() and fix the following checkpatch.pl warning for the whole fifo structure: WARNING: please, no spaces at the start of a line [1] https://en.wikipedia.org/wiki/Flexible_array_member [2] https://github.com/KSPP/linux/issues/79 Tested-by: kernel test robot Link: https://github.com/GustavoARSilva/linux-hardening/blob/master/cii/kernel-ci/hpilo-20200714.md Signed-off-by: Gustavo A. R. Silva Link: https://lore.kernel.org/r/20200714154449.GA26153@embeddedor Signed-off-by: Greg Kroah-Hartman --- drivers/misc/hpilo.c | 2 +- drivers/misc/hpilo.h | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/misc/hpilo.c b/drivers/misc/hpilo.c index 927309b86bab..10c975662f8b 100644 --- a/drivers/misc/hpilo.c +++ b/drivers/misc/hpilo.c @@ -207,7 +207,7 @@ static void ctrl_setup(struct ccb *ccb, int nr_desc, int l2desc_sz) static inline int fifo_sz(int nr_entry) { /* size of a fifo is determined by the number of entries it contains */ - return (nr_entry * sizeof(u64)) + FIFOHANDLESIZE; + return nr_entry * sizeof(u64) + FIFOHANDLESIZE; } static void fifo_setup(void *base_addr, int nr_entry) diff --git a/drivers/misc/hpilo.h b/drivers/misc/hpilo.h index 1aa433a7f66c..f69ff645cac9 100644 --- a/drivers/misc/hpilo.h +++ b/drivers/misc/hpilo.h @@ -160,23 +160,23 @@ struct ccb_data { #define ILO_START_ALIGN 4096 #define ILO_CACHE_SZ 128 struct fifo { - u64 nrents; /* user requested number of fifo entries */ - u64 imask; /* mask to extract valid fifo index */ - u64 merge; /* O/C bits to merge in during enqueue operation */ - u64 reset; /* set to non-zero when the target device resets */ - u8 pad_0[ILO_CACHE_SZ - (sizeof(u64) * 4)]; + u64 nrents; /* user requested number of fifo entries */ + u64 imask; /* mask to extract valid fifo index */ + u64 merge; /* O/C bits to merge in during enqueue operation */ + u64 reset; /* set to non-zero when the target device resets */ + u8 pad_0[ILO_CACHE_SZ - (sizeof(u64) * 4)]; - u64 head; - u8 pad_1[ILO_CACHE_SZ - (sizeof(u64))]; + u64 head; + u8 pad_1[ILO_CACHE_SZ - (sizeof(u64))]; - u64 tail; - u8 pad_2[ILO_CACHE_SZ - (sizeof(u64))]; + u64 tail; + u8 pad_2[ILO_CACHE_SZ - (sizeof(u64))]; - u64 fifobar[1]; + u64 fifobar[]; }; /* convert between struct fifo, and the fifobar, which is saved in the ccb */ -#define FIFOHANDLESIZE (sizeof(struct fifo) - sizeof(u64)) +#define FIFOHANDLESIZE (sizeof(struct fifo)) #define FIFOBARTOHANDLE(_fifo) \ ((struct fifo *)(((char *)(_fifo)) - FIFOHANDLESIZE)) -- cgit v1.2.3-59-g8ed1b From c5e3c684c3d8c1644c7f366a4b08b12cebec4c75 Mon Sep 17 00:00:00 2001 From: Sanyog Kale Date: Sat, 4 Jul 2020 00:56:44 +0530 Subject: MAINTAINERS: change SoundWire maintainer Add Bard as SoundWire maintainer from Intel and change Sanyog's role as reviewer. Signed-off-by: Bard Liao Signed-off-by: Sanyog Kale Acked-by: Bard Liao Link: https://lore.kernel.org/r/20200703192644.751-1-sanyog.r.kale@intel.com Signed-off-by: Vinod Koul --- MAINTAINERS | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 68f21d46614c..5c02cbebe178 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15999,8 +15999,9 @@ F: sound/soc/sof/ SOUNDWIRE SUBSYSTEM M: Vinod Koul -M: Sanyog Kale +M: Bard Liao R: Pierre-Louis Bossart +R: Sanyog Kale L: alsa-devel@alsa-project.org (moderated for non-subscribers) S: Supported F: Documentation/driver-api/soundwire/ -- cgit v1.2.3-59-g8ed1b From 09553140c8d7be7d74e2e39796f644cc085ab273 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 1 Jul 2020 02:43:52 +0800 Subject: soundwire: intel: implement get_sdw_stream() operations This is needed to retrieve the information when the stream is allocated at the dai_link level. Signed-off-by: Pierre-Louis Bossart Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20200630184356.24939-2-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/intel.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 950e4cf09f2a..3f2f23cf8020 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -883,6 +883,22 @@ static int intel_pdm_set_sdw_stream(struct snd_soc_dai *dai, return cdns_set_sdw_stream(dai, stream, false, direction); } +static void *intel_get_sdw_stream(struct snd_soc_dai *dai, + int direction) +{ + struct sdw_cdns_dma_data *dma; + + if (direction == SNDRV_PCM_STREAM_PLAYBACK) + dma = dai->playback_dma_data; + else + dma = dai->capture_dma_data; + + if (!dma) + return NULL; + + return dma->stream; +} + static const struct snd_soc_dai_ops intel_pcm_dai_ops = { .startup = intel_startup, .hw_params = intel_hw_params, @@ -891,6 +907,7 @@ static const struct snd_soc_dai_ops intel_pcm_dai_ops = { .hw_free = intel_hw_free, .shutdown = intel_shutdown, .set_sdw_stream = intel_pcm_set_sdw_stream, + .get_sdw_stream = intel_get_sdw_stream, }; static const struct snd_soc_dai_ops intel_pdm_dai_ops = { @@ -901,6 +918,7 @@ static const struct snd_soc_dai_ops intel_pdm_dai_ops = { .hw_free = intel_hw_free, .shutdown = intel_shutdown, .set_sdw_stream = intel_pdm_set_sdw_stream, + .get_sdw_stream = intel_get_sdw_stream, }; static const struct snd_soc_component_driver dai_component = { -- cgit v1.2.3-59-g8ed1b From 4550569bd779f25398503ad5556f8dc7c1f216c2 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 1 Jul 2020 02:43:53 +0800 Subject: soundwire: stream: add helper to startup/shutdown streams To handle streams at the dailink level, expose two helpers that will be called from machine drivers. Reviewed-by: Ranjani Sridharan Reviewed-by: Guennadi Liakhovetski Reviewed-by: Kai Vehmanen Signed-off-by: Pierre-Louis Bossart Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20200630184356.24939-3-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- Documentation/driver-api/soundwire/stream.rst | 11 ++- drivers/soundwire/stream.c | 98 +++++++++++++++++++++++++++ include/linux/soundwire/sdw.h | 2 + 3 files changed, 110 insertions(+), 1 deletion(-) diff --git a/Documentation/driver-api/soundwire/stream.rst b/Documentation/driver-api/soundwire/stream.rst index 1b386076402c..8858cea7bfe0 100644 --- a/Documentation/driver-api/soundwire/stream.rst +++ b/Documentation/driver-api/soundwire/stream.rst @@ -293,6 +293,10 @@ per stream. From ASoC DPCM framework, this stream state maybe linked to int sdw_alloc_stream(char * stream_name); +The SoundWire core provides a sdw_startup_stream() helper function, +typically called during a dailink .startup() callback, which performs +stream allocation and sets the stream pointer for all DAIs +connected to a stream. SDW_STREAM_CONFIGURED ~~~~~~~~~~~~~~~~~~~~~ @@ -509,7 +513,12 @@ In .shutdown() the data structure maintaining stream state are freed up. void sdw_release_stream(struct sdw_stream_runtime * stream); -Not Supported +The SoundWire core provides a sdw_shutdown_stream() helper function, +typically called during a dailink .shutdown() callback, which clears +the stream pointer for all DAIS connected to a stream and releases the +memory allocated for the stream. + + Not Supported ============= 1. A single port with multiple channels supported cannot be used between two diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index a9a72574b34a..6bc2ff29c202 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -13,6 +13,7 @@ #include #include #include +#include #include "bus.h" /* @@ -1826,3 +1827,100 @@ state_err: return ret; } EXPORT_SYMBOL(sdw_deprepare_stream); + +static int set_stream(struct snd_pcm_substream *substream, + struct sdw_stream_runtime *sdw_stream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *dai; + int ret = 0; + int i; + + /* Set stream pointer on all DAIs */ + for_each_rtd_dais(rtd, i, dai) { + ret = snd_soc_dai_set_sdw_stream(dai, sdw_stream, substream->stream); + if (ret < 0) { + dev_err(rtd->dev, "failed to set stream pointer on dai %s", dai->name); + break; + } + } + + return ret; +} + +/** + * sdw_startup_stream() - Startup SoundWire stream + * + * @stream: Soundwire stream + * + * Documentation/driver-api/soundwire/stream.rst explains this API in detail + */ +int sdw_startup_stream(void *sdw_substream) +{ + struct snd_pcm_substream *substream = sdw_substream; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct sdw_stream_runtime *sdw_stream; + char *name; + int ret; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + name = kasprintf(GFP_KERNEL, "%s-Playback", substream->name); + else + name = kasprintf(GFP_KERNEL, "%s-Capture", substream->name); + + if (!name) + return -ENOMEM; + + sdw_stream = sdw_alloc_stream(name); + if (!sdw_stream) { + dev_err(rtd->dev, "alloc stream failed for substream DAI %s", substream->name); + ret = -ENOMEM; + goto error; + } + + ret = set_stream(substream, sdw_stream); + if (ret < 0) + goto release_stream; + return 0; + +release_stream: + sdw_release_stream(sdw_stream); + set_stream(substream, NULL); +error: + kfree(name); + return ret; +} +EXPORT_SYMBOL(sdw_startup_stream); + +/** + * sdw_shutdown_stream() - Shutdown SoundWire stream + * + * @stream: Soundwire stream + * + * Documentation/driver-api/soundwire/stream.rst explains this API in detail + */ +void sdw_shutdown_stream(void *sdw_substream) +{ + struct snd_pcm_substream *substream = sdw_substream; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct sdw_stream_runtime *sdw_stream; + struct snd_soc_dai *dai; + + /* Find stream from first CPU DAI */ + dai = asoc_rtd_to_cpu(rtd, 0); + + sdw_stream = snd_soc_dai_get_sdw_stream(dai, substream->stream); + + if (!sdw_stream) { + dev_err(rtd->dev, "no stream found for DAI %s", dai->name); + return; + } + + /* release memory */ + kfree(sdw_stream->name); + sdw_release_stream(sdw_stream); + + /* clear DAI data */ + set_stream(substream, NULL); +} +EXPORT_SYMBOL(sdw_shutdown_stream); diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index 64c9314cb903..4057adf7f049 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -955,10 +955,12 @@ int sdw_stream_remove_master(struct sdw_bus *bus, struct sdw_stream_runtime *stream); int sdw_stream_remove_slave(struct sdw_slave *slave, struct sdw_stream_runtime *stream); +int sdw_startup_stream(void *sdw_substream); int sdw_prepare_stream(struct sdw_stream_runtime *stream); int sdw_enable_stream(struct sdw_stream_runtime *stream); int sdw_disable_stream(struct sdw_stream_runtime *stream); int sdw_deprepare_stream(struct sdw_stream_runtime *stream); +void sdw_shutdown_stream(void *sdw_substream); int sdw_bus_prep_clk_stop(struct sdw_bus *bus); int sdw_bus_clk_stop(struct sdw_bus *bus); int sdw_bus_exit_clk_stop(struct sdw_bus *bus); -- cgit v1.2.3-59-g8ed1b From ff16d1e584629f6d2d5ecfe79e46eb5ce5d97b7f Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 1 Jul 2020 02:43:54 +0800 Subject: soundwire: intel: remove stream allocation/free To support streaming across multiple links, the stream allocation/free needs to be at the dailink level, not the dai. Signed-off-by: Pierre-Louis Bossart Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20200630184356.24939-4-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/intel.c | 57 +---------------------------------------------- 1 file changed, 1 insertion(+), 56 deletions(-) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 3f2f23cf8020..2e1e1088a743 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -625,57 +625,6 @@ static int intel_post_bank_switch(struct sdw_bus *bus) * DAI routines */ -static int sdw_stream_setup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct sdw_stream_runtime *sdw_stream = NULL; - char *name; - int i, ret; - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - name = kasprintf(GFP_KERNEL, "%s-Playback", dai->name); - else - name = kasprintf(GFP_KERNEL, "%s-Capture", dai->name); - - if (!name) - return -ENOMEM; - - sdw_stream = sdw_alloc_stream(name); - if (!sdw_stream) { - dev_err(dai->dev, "alloc stream failed for DAI %s", dai->name); - ret = -ENOMEM; - goto error; - } - - /* Set stream pointer on CPU DAI */ - ret = snd_soc_dai_set_sdw_stream(dai, sdw_stream, substream->stream); - if (ret < 0) { - dev_err(dai->dev, "failed to set stream pointer on cpu dai %s", - dai->name); - goto release_stream; - } - - /* Set stream pointer on all CODEC DAIs */ - for (i = 0; i < rtd->num_codecs; i++) { - ret = snd_soc_dai_set_sdw_stream(asoc_rtd_to_codec(rtd, i), sdw_stream, - substream->stream); - if (ret < 0) { - dev_err(dai->dev, "failed to set stream pointer on codec dai %s", - asoc_rtd_to_codec(rtd, i)->name); - goto release_stream; - } - } - - return 0; - -release_stream: - sdw_release_stream(sdw_stream); -error: - kfree(name); - return ret; -} - static int intel_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { @@ -683,8 +632,7 @@ static int intel_startup(struct snd_pcm_substream *substream, * TODO: add pm_runtime support here, the startup callback * will make sure the IP is 'active' */ - - return sdw_stream_setup(substream, dai); + return 0; } static int intel_hw_params(struct snd_pcm_substream *substream, @@ -852,9 +800,6 @@ intel_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) return ret; } - kfree(dma->stream->name); - sdw_release_stream(dma->stream); - return 0; } -- cgit v1.2.3-59-g8ed1b From b5e9e687d6226218d643f3030abb147da25a8f3a Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 1 Jul 2020 02:43:55 +0800 Subject: soundwire: cadence: allocate/free dma_data in set_sdw_stream The current memory allocation is somewhat strange: the dma_data is allocated in set_sdw_stream, but released in the intel DAI shutdown. This no longer works with the multi-cpu implementation, since the dma_data is released in the dai shutdown which takes place before the dailink shutdown. Move to a more symmetric allocation where the dma_data is allocated with non-NULL SoundWire stream, and conversely released when a NULL stream is provided - for consistency with the stream startup and shutdown operations. Signed-off-by: Pierre-Louis Bossart Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20200630184356.24939-5-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/cadence_master.c | 52 ++++++++++++++++++++++++++++---------- 1 file changed, 38 insertions(+), 14 deletions(-) diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 9ea87538b9ef..613dbd415b91 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -1437,25 +1437,49 @@ int cdns_set_sdw_stream(struct snd_soc_dai *dai, struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); struct sdw_cdns_dma_data *dma; - dma = kzalloc(sizeof(*dma), GFP_KERNEL); - if (!dma) - return -ENOMEM; + if (stream) { + /* first paranoia check */ + if (direction == SNDRV_PCM_STREAM_PLAYBACK) + dma = dai->playback_dma_data; + else + dma = dai->capture_dma_data; + + if (dma) { + dev_err(dai->dev, + "dma_data already allocated for dai %s\n", + dai->name); + return -EINVAL; + } - if (pcm) - dma->stream_type = SDW_STREAM_PCM; - else - dma->stream_type = SDW_STREAM_PDM; + /* allocate and set dma info */ + dma = kzalloc(sizeof(*dma), GFP_KERNEL); + if (!dma) + return -ENOMEM; - dma->bus = &cdns->bus; - dma->link_id = cdns->instance; + if (pcm) + dma->stream_type = SDW_STREAM_PCM; + else + dma->stream_type = SDW_STREAM_PDM; - dma->stream = stream; + dma->bus = &cdns->bus; + dma->link_id = cdns->instance; - if (direction == SNDRV_PCM_STREAM_PLAYBACK) - dai->playback_dma_data = dma; - else - dai->capture_dma_data = dma; + dma->stream = stream; + if (direction == SNDRV_PCM_STREAM_PLAYBACK) + dai->playback_dma_data = dma; + else + dai->capture_dma_data = dma; + } else { + /* for NULL stream we release allocated dma_data */ + if (direction == SNDRV_PCM_STREAM_PLAYBACK) { + kfree(dai->playback_dma_data); + dai->playback_dma_data = NULL; + } else { + kfree(dai->capture_dma_data); + dai->capture_dma_data = NULL; + } + } return 0; } EXPORT_SYMBOL(cdns_set_sdw_stream); -- cgit v1.2.3-59-g8ed1b From 9cb834b17cb2b1df5cb4389c5830b657d66b3e56 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 1 Jul 2020 02:43:56 +0800 Subject: soundwire: intel: don't free dma_data in DAI shutdown Now that the DMA data is allocated/freed in set_sdw_stream(), remove free operations. Signed-off-by: Pierre-Louis Bossart Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20200630184356.24939-6-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/intel.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 2e1e1088a743..7a65414e5714 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -806,14 +806,7 @@ intel_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) static void intel_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct sdw_cdns_dma_data *dma; - - dma = snd_soc_dai_get_dma_data(dai, substream); - if (!dma) - return; - snd_soc_dai_set_dma_data(dai, substream, NULL); - kfree(dma); } static int intel_pcm_set_sdw_stream(struct snd_soc_dai *dai, -- cgit v1.2.3-59-g8ed1b From a8209dd42a60dca0fd15bc73d19fc4009e704c17 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 15 Jul 2020 05:37:43 +0800 Subject: soundwire: sdw.h: fix PRBS/Static_1 swapped definitions Table 110 "Port Data Modes" of the SoundWire 1.2 specification lists PRBS as b01 and Static_1 as b11. The existing headers swapped the two values, fix. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Guennadi Liakhovetski Reviewed-by: Rander Wang Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20200714213744.24674-2-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- include/linux/soundwire/sdw.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index 4057adf7f049..6452bac957b3 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -152,19 +152,19 @@ enum sdw_data_direction { * * @SDW_PORT_DATA_MODE_NORMAL: Normal data mode where audio data is received * and transmitted. + * @SDW_PORT_DATA_MODE_PRBS: Test mode which uses a PRBS generator to produce + * a pseudo random data pattern that is transferred + * @SDW_PORT_DATA_MODE_STATIC_0: Simple test mode which uses static value of + * logic 0. The encoding will result in no signal transitions * @SDW_PORT_DATA_MODE_STATIC_1: Simple test mode which uses static value of * logic 1. The encoding will result in signal transitions at every bitslot * owned by this Port - * @SDW_PORT_DATA_MODE_STATIC_0: Simple test mode which uses static value of - * logic 0. The encoding will result in no signal transitions - * @SDW_PORT_DATA_MODE_PRBS: Test mode which uses a PRBS generator to produce - * a pseudo random data pattern that is transferred */ enum sdw_port_data_mode { SDW_PORT_DATA_MODE_NORMAL = 0, - SDW_PORT_DATA_MODE_STATIC_1 = 1, + SDW_PORT_DATA_MODE_PRBS = 1, SDW_PORT_DATA_MODE_STATIC_0 = 2, - SDW_PORT_DATA_MODE_PRBS = 3, + SDW_PORT_DATA_MODE_STATIC_1 = 3, }; /* -- cgit v1.2.3-59-g8ed1b From 9256686898881b0694fe6af2c3283b69677838de Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 15 Jul 2020 05:37:44 +0800 Subject: soundwire: sdw.h: fix indentation Not sure how this went undetected for years. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Guennadi Liakhovetski Reviewed-by: Rander Wang Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20200714213744.24674-3-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- include/linux/soundwire/sdw.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index 6452bac957b3..76052f12c9f7 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -88,10 +88,10 @@ enum sdw_slave_status { * @SDW_CLK_POST_DEPREPARE: post clock stop de-prepare */ enum sdw_clk_stop_type { - SDW_CLK_PRE_PREPARE = 0, - SDW_CLK_POST_PREPARE, - SDW_CLK_PRE_DEPREPARE, - SDW_CLK_POST_DEPREPARE, + SDW_CLK_PRE_PREPARE = 0, + SDW_CLK_POST_PREPARE, + SDW_CLK_PRE_DEPREPARE, + SDW_CLK_POST_DEPREPARE, }; /** -- cgit v1.2.3-59-g8ed1b From 3b71c69024a7d9700e33fd1f914d689151f7992d Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Wed, 15 Jul 2020 15:27:02 +0530 Subject: soundwire: fix the kernel-doc comment sdw_startup_stream() and sdw_shutdown_stream() argument has been updated but not the comments, so update these as well to fix warning with W=1 drivers/soundwire/stream.c:1859: warning: Function parameter or member 'sdw_substream' not described in 'sdw_startup_stream' drivers/soundwire/stream.c:1859: warning: Excess function parameter 'stream' description in 'sdw_startup_stream' drivers/soundwire/stream.c:1903: warning: Function parameter or member 'sdw_substream' not described in 'sdw_shutdown_stream' drivers/soundwire/stream.c:1903: warning: Excess function parameter 'stream' description in 'sdw_shutdown_stream' Signed-off-by: Vinod Koul Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200715095702.1519554-1-vkoul@kernel.org Signed-off-by: Vinod Koul --- drivers/soundwire/stream.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index 6bc2ff29c202..37290a799023 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -1851,7 +1851,7 @@ static int set_stream(struct snd_pcm_substream *substream, /** * sdw_startup_stream() - Startup SoundWire stream * - * @stream: Soundwire stream + * @sdw_substream: Soundwire stream * * Documentation/driver-api/soundwire/stream.rst explains this API in detail */ @@ -1895,7 +1895,7 @@ EXPORT_SYMBOL(sdw_startup_stream); /** * sdw_shutdown_stream() - Shutdown SoundWire stream * - * @stream: Soundwire stream + * @sdw_substream: Soundwire stream * * Documentation/driver-api/soundwire/stream.rst explains this API in detail */ -- cgit v1.2.3-59-g8ed1b From 8b34a2895bb7f791ce6bebc1737373e9475c58b2 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Thu, 16 Jul 2020 11:54:27 +0530 Subject: phy: sun4i-usb: explicitly include gpio/consumer.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Driver uses GPIO functions but doesn't include the header explcitly. Add that to fix build errors when GPIOLIB is disabled. drivers/phy/allwinner/phy-sun4i-usb.c:367:11: error: implicit declaration of function ‘gpiod_get_value_cansleep’; did you mean ‘gpio_get_value_cansleep’? [-Werror=implicit-function-declaration] drivers/phy/allwinner/phy-sun4i-usb.c:707:22: error: implicit declaration of function ‘devm_gpiod_get_optional’; did you mean ‘devm_clk_get_optional’? [-Werror=implicit-function-declaration] drivers/phy/allwinner/phy-sun4i-usb.c:708:11: error: ‘GPIOD_IN’ undeclared (first use in this function); did you mean ‘GPIOF_IN’? drivers/phy/allwinner/phy-sun4i-usb.c:815:21: error: implicit declaration of function ‘gpiod_to_irq’; did you mean ‘gpio_to_irq’? [-Werror=implicit-function-declaration] Reported-by: Randy Dunlap Signed-off-by: Vinod Koul Acked-by: Chen-Yu Tsai Acked-by: Randy Dunlap # build-tested Link: https://lore.kernel.org/r/20200716062427.71763-1-vkoul@kernel.org --- drivers/phy/allwinner/phy-sun4i-usb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/phy/allwinner/phy-sun4i-usb.c b/drivers/phy/allwinner/phy-sun4i-usb.c index 7e09ad6a0b42..585d0a59e712 100644 --- a/drivers/phy/allwinner/phy-sun4i-usb.c +++ b/drivers/phy/allwinner/phy-sun4i-usb.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include -- cgit v1.2.3-59-g8ed1b From 170ba9c23f0b813f9ba53a9cd3fec0f3f95f45aa Mon Sep 17 00:00:00 2001 From: Alim Akhtar Date: Fri, 17 Jul 2020 00:52:16 +0530 Subject: dt-bindings: phy: Document Samsung UFS PHY bindings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch documents Samsung UFS PHY device tree bindings Reviewed-by: Rob Herring Signed-off-by: Alim Akhtar Tested-by: Paweł Chmiel Link: https://lore.kernel.org/r/20200716192217.35740-1-alim.akhtar@samsung.com Signed-off-by: Vinod Koul --- .../devicetree/bindings/phy/samsung,ufs-phy.yaml | 75 ++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/samsung,ufs-phy.yaml diff --git a/Documentation/devicetree/bindings/phy/samsung,ufs-phy.yaml b/Documentation/devicetree/bindings/phy/samsung,ufs-phy.yaml new file mode 100644 index 000000000000..636cc501b54f --- /dev/null +++ b/Documentation/devicetree/bindings/phy/samsung,ufs-phy.yaml @@ -0,0 +1,75 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/samsung,ufs-phy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Samsung SoC series UFS PHY Device Tree Bindings + +maintainers: + - Alim Akhtar + +properties: + "#phy-cells": + const: 0 + + compatible: + enum: + - samsung,exynos7-ufs-phy + + reg: + maxItems: 1 + + reg-names: + items: + - const: phy-pma + + clocks: + items: + - description: PLL reference clock + - description: symbol clock for input symbol ( rx0-ch0 symbol clock) + - description: symbol clock for input symbol ( rx1-ch1 symbol clock) + - description: symbol clock for output symbol ( tx0 symbol clock) + + clock-names: + items: + - const: ref_clk + - const: rx1_symbol_clk + - const: rx0_symbol_clk + - const: tx0_symbol_clk + + samsung,pmu-syscon: + $ref: '/schemas/types.yaml#/definitions/phandle' + description: phandle for PMU system controller interface, used to + control pmu registers bits for ufs m-phy + +required: + - "#phy-cells" + - compatible + - reg + - reg-names + - clocks + - clock-names + - samsung,pmu-syscon + +additionalProperties: false + +examples: + - | + #include + + ufs_phy: ufs-phy@15571800 { + compatible = "samsung,exynos7-ufs-phy"; + reg = <0x15571800 0x240>; + reg-names = "phy-pma"; + samsung,pmu-syscon = <&pmu_system_controller>; + #phy-cells = <0>; + clocks = <&clock_fsys1 SCLK_COMBO_PHY_EMBEDDED_26M>, + <&clock_fsys1 PHYCLK_UFS20_RX1_SYMBOL_USER>, + <&clock_fsys1 PHYCLK_UFS20_RX0_SYMBOL_USER>, + <&clock_fsys1 PHYCLK_UFS20_TX0_SYMBOL_USER>; + clock-names = "ref_clk", "rx1_symbol_clk", + "rx0_symbol_clk", "tx0_symbol_clk"; + + }; +... -- cgit v1.2.3-59-g8ed1b From bca21e930451416f92c0c4c679feb9d8fa33c08f Mon Sep 17 00:00:00 2001 From: Alim Akhtar Date: Fri, 17 Jul 2020 00:52:17 +0530 Subject: phy: samsung-ufs: add UFS PHY driver for samsung SoC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch introduces Samsung UFS PHY driver. This driver supports to deal with phy calibration and power control according to UFS host driver's behavior. [Robot: -Wmissing-prototypes and -Wsometimes-uninitialized] Reported-by: kernel test robot Reviewed-by: Kiwoong Kim Signed-off-by: Seungwon Jeon Signed-off-by: Alim Akhtar Cc: Kishon Vijay Abraham I Cc: Vinod Koul Tested-by: Paweł Chmiel Link: https://lore.kernel.org/r/20200716192217.35740-2-alim.akhtar@samsung.com Signed-off-by: Vinod Koul --- drivers/phy/samsung/Kconfig | 9 + drivers/phy/samsung/Makefile | 1 + drivers/phy/samsung/phy-exynos7-ufs.h | 81 ++++++++ drivers/phy/samsung/phy-samsung-ufs.c | 366 ++++++++++++++++++++++++++++++++++ drivers/phy/samsung/phy-samsung-ufs.h | 139 +++++++++++++ 5 files changed, 596 insertions(+) create mode 100644 drivers/phy/samsung/phy-exynos7-ufs.h create mode 100644 drivers/phy/samsung/phy-samsung-ufs.c create mode 100644 drivers/phy/samsung/phy-samsung-ufs.h diff --git a/drivers/phy/samsung/Kconfig b/drivers/phy/samsung/Kconfig index 19f2e3119343..e20d2fcc9fe7 100644 --- a/drivers/phy/samsung/Kconfig +++ b/drivers/phy/samsung/Kconfig @@ -29,6 +29,15 @@ config PHY_EXYNOS_PCIE Enable PCIe PHY support for Exynos SoC series. This driver provides PHY interface for Exynos PCIe controller. +config PHY_SAMSUNG_UFS + tristate "SAMSUNG SoC series UFS PHY driver" + depends on OF && (ARCH_EXYNOS || COMPILE_TEST) + select GENERIC_PHY + help + Enable this to support the Samsung UFS PHY driver for + Samsung SoCs. This driver provides the interface for UFS + host controller to do PHY related programming. + config PHY_SAMSUNG_USB2 tristate "Samsung USB 2.0 PHY driver" depends on HAS_IOMEM diff --git a/drivers/phy/samsung/Makefile b/drivers/phy/samsung/Makefile index db9b1aa0de6e..3959100fe8a2 100644 --- a/drivers/phy/samsung/Makefile +++ b/drivers/phy/samsung/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO) += phy-exynos-dp-video.o obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o obj-$(CONFIG_PHY_EXYNOS_PCIE) += phy-exynos-pcie.o +obj-$(CONFIG_PHY_SAMSUNG_UFS) += phy-samsung-ufs.o obj-$(CONFIG_PHY_SAMSUNG_USB2) += phy-exynos-usb2.o phy-exynos-usb2-y += phy-samsung-usb2.o phy-exynos-usb2-$(CONFIG_PHY_EXYNOS4210_USB2) += phy-exynos4210-usb2.o diff --git a/drivers/phy/samsung/phy-exynos7-ufs.h b/drivers/phy/samsung/phy-exynos7-ufs.h new file mode 100644 index 000000000000..518923141958 --- /dev/null +++ b/drivers/phy/samsung/phy-exynos7-ufs.h @@ -0,0 +1,81 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * UFS PHY driver data for Samsung EXYNOS7 SoC + * + * Copyright (C) 2020 Samsung Electronics Co., Ltd. + */ +#ifndef _PHY_EXYNOS7_UFS_H_ +#define _PHY_EXYNOS7_UFS_H_ + +#include "phy-samsung-ufs.h" + +#define EXYNOS7_EMBEDDED_COMBO_PHY_CTRL 0x720 +#define EXYNOS7_EMBEDDED_COMBO_PHY_CTRL_MASK 0x1 +#define EXYNOS7_EMBEDDED_COMBO_PHY_CTRL_EN BIT(0) + +/* Calibration for phy initialization */ +static const struct samsung_ufs_phy_cfg exynos7_pre_init_cfg[] = { + PHY_COMN_REG_CFG(0x00f, 0xfa, PWR_MODE_ANY), + PHY_COMN_REG_CFG(0x010, 0x82, PWR_MODE_ANY), + PHY_COMN_REG_CFG(0x011, 0x1e, PWR_MODE_ANY), + PHY_COMN_REG_CFG(0x017, 0x84, PWR_MODE_ANY), + PHY_TRSV_REG_CFG(0x035, 0x58, PWR_MODE_ANY), + PHY_TRSV_REG_CFG(0x036, 0x32, PWR_MODE_ANY), + PHY_TRSV_REG_CFG(0x037, 0x40, PWR_MODE_ANY), + PHY_TRSV_REG_CFG(0x03b, 0x83, PWR_MODE_ANY), + PHY_TRSV_REG_CFG(0x042, 0x88, PWR_MODE_ANY), + PHY_TRSV_REG_CFG(0x043, 0xa6, PWR_MODE_ANY), + PHY_TRSV_REG_CFG(0x048, 0x74, PWR_MODE_ANY), + PHY_TRSV_REG_CFG(0x04c, 0x5b, PWR_MODE_ANY), + PHY_TRSV_REG_CFG(0x04d, 0x83, PWR_MODE_ANY), + PHY_TRSV_REG_CFG(0x05c, 0x14, PWR_MODE_ANY), + END_UFS_PHY_CFG +}; + +/* Calibration for HS mode series A/B */ +static const struct samsung_ufs_phy_cfg exynos7_pre_pwr_hs_cfg[] = { + PHY_COMN_REG_CFG(0x00f, 0xfa, PWR_MODE_HS_ANY), + PHY_COMN_REG_CFG(0x010, 0x82, PWR_MODE_HS_ANY), + PHY_COMN_REG_CFG(0x011, 0x1e, PWR_MODE_HS_ANY), + /* Setting order: 1st(0x16, 2nd(0x15) */ + PHY_COMN_REG_CFG(0x016, 0xff, PWR_MODE_HS_ANY), + PHY_COMN_REG_CFG(0x015, 0x80, PWR_MODE_HS_ANY), + PHY_COMN_REG_CFG(0x017, 0x94, PWR_MODE_HS_ANY), + PHY_TRSV_REG_CFG(0x036, 0x32, PWR_MODE_HS_ANY), + PHY_TRSV_REG_CFG(0x037, 0x43, PWR_MODE_HS_ANY), + PHY_TRSV_REG_CFG(0x038, 0x3f, PWR_MODE_HS_ANY), + PHY_TRSV_REG_CFG(0x042, 0x88, PWR_MODE_HS_G2_SER_A), + PHY_TRSV_REG_CFG(0x042, 0xbb, PWR_MODE_HS_G2_SER_B), + PHY_TRSV_REG_CFG(0x043, 0xa6, PWR_MODE_HS_ANY), + PHY_TRSV_REG_CFG(0x048, 0x74, PWR_MODE_HS_ANY), + PHY_TRSV_REG_CFG(0x034, 0x35, PWR_MODE_HS_G2_SER_A), + PHY_TRSV_REG_CFG(0x034, 0x36, PWR_MODE_HS_G2_SER_B), + PHY_TRSV_REG_CFG(0x035, 0x5b, PWR_MODE_HS_G2_SER_A), + PHY_TRSV_REG_CFG(0x035, 0x5c, PWR_MODE_HS_G2_SER_B), + END_UFS_PHY_CFG +}; + +/* Calibration for HS mode series A/B atfer PMC */ +static const struct samsung_ufs_phy_cfg exynos7_post_pwr_hs_cfg[] = { + PHY_COMN_REG_CFG(0x015, 0x00, PWR_MODE_HS_ANY), + PHY_TRSV_REG_CFG(0x04d, 0x83, PWR_MODE_HS_ANY), + END_UFS_PHY_CFG +}; + +static const struct samsung_ufs_phy_cfg *exynos7_ufs_phy_cfgs[CFG_TAG_MAX] = { + [CFG_PRE_INIT] = exynos7_pre_init_cfg, + [CFG_PRE_PWR_HS] = exynos7_pre_pwr_hs_cfg, + [CFG_POST_PWR_HS] = exynos7_post_pwr_hs_cfg, +}; + +static struct samsung_ufs_phy_drvdata exynos7_ufs_phy = { + .cfg = exynos7_ufs_phy_cfgs, + .isol = { + .offset = EXYNOS7_EMBEDDED_COMBO_PHY_CTRL, + .mask = EXYNOS7_EMBEDDED_COMBO_PHY_CTRL_MASK, + .en = EXYNOS7_EMBEDDED_COMBO_PHY_CTRL_EN, + }, + .has_symbol_clk = 1, +}; + +#endif /* _PHY_EXYNOS7_UFS_H_ */ diff --git a/drivers/phy/samsung/phy-samsung-ufs.c b/drivers/phy/samsung/phy-samsung-ufs.c new file mode 100644 index 000000000000..43ef77d1d96c --- /dev/null +++ b/drivers/phy/samsung/phy-samsung-ufs.c @@ -0,0 +1,366 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * UFS PHY driver for Samsung SoC + * + * Copyright (C) 2020 Samsung Electronics Co., Ltd. + * Author: Seungwon Jeon + * Author: Alim Akhtar + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "phy-samsung-ufs.h" + +#define for_each_phy_lane(phy, i) \ + for (i = 0; i < (phy)->lane_cnt; i++) +#define for_each_phy_cfg(cfg) \ + for (; (cfg)->id; (cfg)++) + +#define PHY_DEF_LANE_CNT 1 + +static void samsung_ufs_phy_config(struct samsung_ufs_phy *phy, + const struct samsung_ufs_phy_cfg *cfg, + u8 lane) +{ + enum {LANE_0, LANE_1}; /* lane index */ + + switch (lane) { + case LANE_0: + writel(cfg->val, (phy)->reg_pma + cfg->off_0); + break; + case LANE_1: + if (cfg->id == PHY_TRSV_BLK) + writel(cfg->val, (phy)->reg_pma + cfg->off_1); + break; + } +} + +static int samsung_ufs_phy_wait_for_lock_acq(struct phy *phy) +{ + struct samsung_ufs_phy *ufs_phy = get_samsung_ufs_phy(phy); + const unsigned int timeout_us = 100000; + const unsigned int sleep_us = 10; + u32 val; + int err; + + err = readl_poll_timeout( + ufs_phy->reg_pma + PHY_APB_ADDR(PHY_PLL_LOCK_STATUS), + val, (val & PHY_PLL_LOCK_BIT), sleep_us, timeout_us); + if (err) { + dev_err(ufs_phy->dev, + "failed to get phy pll lock acquisition %d\n", err); + goto out; + } + + err = readl_poll_timeout( + ufs_phy->reg_pma + PHY_APB_ADDR(PHY_CDR_LOCK_STATUS), + val, (val & PHY_CDR_LOCK_BIT), sleep_us, timeout_us); + if (err) + dev_err(ufs_phy->dev, + "failed to get phy cdr lock acquisition %d\n", err); +out: + return err; +} + +static int samsung_ufs_phy_calibrate(struct phy *phy) +{ + struct samsung_ufs_phy *ufs_phy = get_samsung_ufs_phy(phy); + struct samsung_ufs_phy_cfg **cfgs = ufs_phy->cfg; + const struct samsung_ufs_phy_cfg *cfg; + int err = 0; + int i; + + if (unlikely(ufs_phy->ufs_phy_state < CFG_PRE_INIT || + ufs_phy->ufs_phy_state >= CFG_TAG_MAX)) { + dev_err(ufs_phy->dev, "invalid phy config index %d\n", ufs_phy->ufs_phy_state); + return -EINVAL; + } + + cfg = cfgs[ufs_phy->ufs_phy_state]; + if (!cfg) + goto out; + + for_each_phy_cfg(cfg) { + for_each_phy_lane(ufs_phy, i) { + samsung_ufs_phy_config(ufs_phy, cfg, i); + } + } + + if (ufs_phy->ufs_phy_state == CFG_POST_PWR_HS) + err = samsung_ufs_phy_wait_for_lock_acq(phy); + + /** + * In Samsung ufshci, PHY need to be calibrated at different + * stages / state mainly before Linkstartup, after Linkstartup, + * before power mode change and after power mode change. + * Below state machine to make sure to calibrate PHY in each + * state. Here after configuring PHY in a given state, will + * change the state to next state so that next state phy + * calibration value can be programed + */ +out: + switch (ufs_phy->ufs_phy_state) { + case CFG_PRE_INIT: + ufs_phy->ufs_phy_state = CFG_POST_INIT; + break; + case CFG_POST_INIT: + ufs_phy->ufs_phy_state = CFG_PRE_PWR_HS; + break; + case CFG_PRE_PWR_HS: + ufs_phy->ufs_phy_state = CFG_POST_PWR_HS; + break; + case CFG_POST_PWR_HS: + /* Change back to INIT state */ + ufs_phy->ufs_phy_state = CFG_PRE_INIT; + break; + default: + dev_err(ufs_phy->dev, "wrong state for phy calibration\n"); + } + + return err; +} + +static int samsung_ufs_phy_symbol_clk_init(struct samsung_ufs_phy *phy) +{ + int ret; + + phy->tx0_symbol_clk = devm_clk_get(phy->dev, "tx0_symbol_clk"); + if (IS_ERR(phy->tx0_symbol_clk)) { + dev_err(phy->dev, "failed to get tx0_symbol_clk clock\n"); + return PTR_ERR(phy->tx0_symbol_clk); + } + + phy->rx0_symbol_clk = devm_clk_get(phy->dev, "rx0_symbol_clk"); + if (IS_ERR(phy->rx0_symbol_clk)) { + dev_err(phy->dev, "failed to get rx0_symbol_clk clock\n"); + return PTR_ERR(phy->rx0_symbol_clk); + } + + phy->rx1_symbol_clk = devm_clk_get(phy->dev, "rx1_symbol_clk"); + if (IS_ERR(phy->rx0_symbol_clk)) { + dev_err(phy->dev, "failed to get rx1_symbol_clk clock\n"); + return PTR_ERR(phy->rx1_symbol_clk); + } + + ret = clk_prepare_enable(phy->tx0_symbol_clk); + if (ret) { + dev_err(phy->dev, "%s: tx0_symbol_clk enable failed %d\n", __func__, ret); + goto out; + } + + ret = clk_prepare_enable(phy->rx0_symbol_clk); + if (ret) { + dev_err(phy->dev, "%s: rx0_symbol_clk enable failed %d\n", __func__, ret); + goto out_disable_tx0_clk; + } + + ret = clk_prepare_enable(phy->rx1_symbol_clk); + if (ret) { + dev_err(phy->dev, "%s: rx1_symbol_clk enable failed %d\n", __func__, ret); + goto out_disable_rx0_clk; + } + + return 0; + +out_disable_rx0_clk: + clk_disable_unprepare(phy->rx0_symbol_clk); +out_disable_tx0_clk: + clk_disable_unprepare(phy->tx0_symbol_clk); +out: + return ret; +} + +static int samsung_ufs_phy_clks_init(struct samsung_ufs_phy *phy) +{ + int ret; + + phy->ref_clk = devm_clk_get(phy->dev, "ref_clk"); + if (IS_ERR(phy->ref_clk)) + dev_err(phy->dev, "failed to get ref_clk clock\n"); + + ret = clk_prepare_enable(phy->ref_clk); + if (ret) { + dev_err(phy->dev, "%s: ref_clk enable failed %d\n", __func__, ret); + return ret; + } + + dev_dbg(phy->dev, "UFS MPHY ref_clk_rate = %ld\n", clk_get_rate(phy->ref_clk)); + + return 0; +} + +static int samsung_ufs_phy_init(struct phy *phy) +{ + struct samsung_ufs_phy *ss_phy = get_samsung_ufs_phy(phy); + int ret; + + ss_phy->lane_cnt = phy->attrs.bus_width; + ss_phy->ufs_phy_state = CFG_PRE_INIT; + + if (ss_phy->drvdata->has_symbol_clk) { + ret = samsung_ufs_phy_symbol_clk_init(ss_phy); + if (ret) + dev_err(ss_phy->dev, "failed to set ufs phy symbol clocks\n"); + } + + ret = samsung_ufs_phy_clks_init(ss_phy); + if (ret) + dev_err(ss_phy->dev, "failed to set ufs phy clocks\n"); + + ret = samsung_ufs_phy_calibrate(phy); + if (ret) + dev_err(ss_phy->dev, "ufs phy calibration failed\n"); + + return ret; +} + +static int samsung_ufs_phy_power_on(struct phy *phy) +{ + struct samsung_ufs_phy *ss_phy = get_samsung_ufs_phy(phy); + + samsung_ufs_phy_ctrl_isol(ss_phy, false); + return 0; +} + +static int samsung_ufs_phy_power_off(struct phy *phy) +{ + struct samsung_ufs_phy *ss_phy = get_samsung_ufs_phy(phy); + + samsung_ufs_phy_ctrl_isol(ss_phy, true); + return 0; +} + +static int samsung_ufs_phy_set_mode(struct phy *generic_phy, + enum phy_mode mode, int submode) +{ + struct samsung_ufs_phy *ss_phy = get_samsung_ufs_phy(generic_phy); + + ss_phy->mode = PHY_MODE_INVALID; + + if (mode > 0) + ss_phy->mode = mode; + + return 0; +} + +static int samsung_ufs_phy_exit(struct phy *phy) +{ + struct samsung_ufs_phy *ss_phy = get_samsung_ufs_phy(phy); + + clk_disable_unprepare(ss_phy->ref_clk); + + if (ss_phy->drvdata->has_symbol_clk) { + clk_disable_unprepare(ss_phy->tx0_symbol_clk); + clk_disable_unprepare(ss_phy->rx0_symbol_clk); + clk_disable_unprepare(ss_phy->rx1_symbol_clk); + } + + return 0; +} + +static struct phy_ops samsung_ufs_phy_ops = { + .init = samsung_ufs_phy_init, + .exit = samsung_ufs_phy_exit, + .power_on = samsung_ufs_phy_power_on, + .power_off = samsung_ufs_phy_power_off, + .calibrate = samsung_ufs_phy_calibrate, + .set_mode = samsung_ufs_phy_set_mode, + .owner = THIS_MODULE, +}; + +static const struct of_device_id samsung_ufs_phy_match[]; + +static int samsung_ufs_phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + const struct of_device_id *match; + struct samsung_ufs_phy *phy; + struct phy *gen_phy; + struct phy_provider *phy_provider; + const struct samsung_ufs_phy_drvdata *drvdata; + int err = 0; + + match = of_match_node(samsung_ufs_phy_match, dev->of_node); + if (!match) { + err = -EINVAL; + dev_err(dev, "failed to get match_node\n"); + goto out; + } + + phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); + if (!phy) { + err = -ENOMEM; + goto out; + } + + phy->reg_pma = devm_platform_ioremap_resource_byname(pdev, "phy-pma"); + if (IS_ERR(phy->reg_pma)) { + err = PTR_ERR(phy->reg_pma); + goto out; + } + + phy->reg_pmu = syscon_regmap_lookup_by_phandle( + dev->of_node, "samsung,pmu-syscon"); + if (IS_ERR(phy->reg_pmu)) { + err = PTR_ERR(phy->reg_pmu); + dev_err(dev, "failed syscon remap for pmu\n"); + goto out; + } + + gen_phy = devm_phy_create(dev, NULL, &samsung_ufs_phy_ops); + if (IS_ERR(gen_phy)) { + err = PTR_ERR(gen_phy); + dev_err(dev, "failed to create PHY for ufs-phy\n"); + goto out; + } + + drvdata = match->data; + phy->dev = dev; + phy->drvdata = drvdata; + phy->cfg = (struct samsung_ufs_phy_cfg **)drvdata->cfg; + phy->isol = &drvdata->isol; + phy->lane_cnt = PHY_DEF_LANE_CNT; + + phy_set_drvdata(gen_phy, phy); + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + if (IS_ERR(phy_provider)) { + err = PTR_ERR(phy_provider); + dev_err(dev, "failed to register phy-provider\n"); + goto out; + } +out: + return err; +} + +static const struct of_device_id samsung_ufs_phy_match[] = { + { + .compatible = "samsung,exynos7-ufs-phy", + .data = &exynos7_ufs_phy, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, samsung_ufs_phy_match); + +static struct platform_driver samsung_ufs_phy_driver = { + .probe = samsung_ufs_phy_probe, + .driver = { + .name = "samsung-ufs-phy", + .of_match_table = samsung_ufs_phy_match, + }, +}; +module_platform_driver(samsung_ufs_phy_driver); +MODULE_DESCRIPTION("Samsung SoC UFS PHY Driver"); +MODULE_AUTHOR("Seungwon Jeon "); +MODULE_AUTHOR("Alim Akhtar "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/samsung/phy-samsung-ufs.h b/drivers/phy/samsung/phy-samsung-ufs.h new file mode 100644 index 000000000000..5de78710524c --- /dev/null +++ b/drivers/phy/samsung/phy-samsung-ufs.h @@ -0,0 +1,139 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * UFS PHY driver for Samsung EXYNOS SoC + * + * Copyright (C) 2020 Samsung Electronics Co., Ltd. + * Author: Seungwon Jeon + * Author: Alim Akhtar + * + */ +#ifndef _PHY_SAMSUNG_UFS_ +#define _PHY_SAMSUNG_UFS_ + +#define PHY_COMN_BLK 1 +#define PHY_TRSV_BLK 2 +#define END_UFS_PHY_CFG { 0 } +#define PHY_TRSV_CH_OFFSET 0x30 +#define PHY_APB_ADDR(off) ((off) << 2) + +#define PHY_COMN_REG_CFG(o, v, d) { \ + .off_0 = PHY_APB_ADDR((o)), \ + .off_1 = 0, \ + .val = (v), \ + .desc = (d), \ + .id = PHY_COMN_BLK, \ +} + +#define PHY_TRSV_REG_CFG(o, v, d) { \ + .off_0 = PHY_APB_ADDR((o)), \ + .off_1 = PHY_APB_ADDR((o) + PHY_TRSV_CH_OFFSET), \ + .val = (v), \ + .desc = (d), \ + .id = PHY_TRSV_BLK, \ +} + +/* UFS PHY registers */ +#define PHY_PLL_LOCK_STATUS 0x1e +#define PHY_CDR_LOCK_STATUS 0x5e + +#define PHY_PLL_LOCK_BIT BIT(5) +#define PHY_CDR_LOCK_BIT BIT(4) + +/* description for PHY calibration */ +enum { + /* applicable to any */ + PWR_DESC_ANY = 0, + /* mode */ + PWR_DESC_PWM = 1, + PWR_DESC_HS = 2, + /* series */ + PWR_DESC_SER_A = 1, + PWR_DESC_SER_B = 2, + /* gear */ + PWR_DESC_G1 = 1, + PWR_DESC_G2 = 2, + PWR_DESC_G3 = 3, + /* field mask */ + MD_MASK = 0x3, + SR_MASK = 0x3, + GR_MASK = 0x7, +}; + +#define PWR_MODE_HS_G1_ANY PWR_MODE_HS(PWR_DESC_G1, PWR_DESC_ANY) +#define PWR_MODE_HS_G1_SER_A PWR_MODE_HS(PWR_DESC_G1, PWR_DESC_SER_A) +#define PWR_MODE_HS_G1_SER_B PWR_MODE_HS(PWR_DESC_G1, PWR_DESC_SER_B) +#define PWR_MODE_HS_G2_ANY PWR_MODE_HS(PWR_DESC_G2, PWR_DESC_ANY) +#define PWR_MODE_HS_G2_SER_A PWR_MODE_HS(PWR_DESC_G2, PWR_DESC_SER_A) +#define PWR_MODE_HS_G2_SER_B PWR_MODE_HS(PWR_DESC_G2, PWR_DESC_SER_B) +#define PWR_MODE_HS_G3_ANY PWR_MODE_HS(PWR_DESC_G3, PWR_DESC_ANY) +#define PWR_MODE_HS_G3_SER_A PWR_MODE_HS(PWR_DESC_G3, PWR_DESC_SER_A) +#define PWR_MODE_HS_G3_SER_B PWR_MODE_HS(PWR_DESC_G3, PWR_DESC_SER_B) +#define PWR_MODE(g, s, m) ((((g) & GR_MASK) << 4) |\ + (((s) & SR_MASK) << 2) | ((m) & MD_MASK)) +#define PWR_MODE_PWM_ANY PWR_MODE(PWR_DESC_ANY,\ + PWR_DESC_ANY, PWR_DESC_PWM) +#define PWR_MODE_HS(g, s) ((((g) & GR_MASK) << 4) |\ + (((s) & SR_MASK) << 2) | PWR_DESC_HS) +#define PWR_MODE_HS_ANY PWR_MODE(PWR_DESC_ANY,\ + PWR_DESC_ANY, PWR_DESC_HS) +#define PWR_MODE_ANY PWR_MODE(PWR_DESC_ANY,\ + PWR_DESC_ANY, PWR_DESC_ANY) +/* PHY calibration point/state */ +enum { + CFG_PRE_INIT, + CFG_POST_INIT, + CFG_PRE_PWR_HS, + CFG_POST_PWR_HS, + CFG_TAG_MAX, +}; + +struct samsung_ufs_phy_cfg { + u32 off_0; + u32 off_1; + u32 val; + u8 desc; + u8 id; +}; + +struct samsung_ufs_phy_drvdata { + const struct samsung_ufs_phy_cfg **cfg; + struct pmu_isol { + u32 offset; + u32 mask; + u32 en; + } isol; + bool has_symbol_clk; +}; + +struct samsung_ufs_phy { + struct device *dev; + void __iomem *reg_pma; + struct regmap *reg_pmu; + struct clk *ref_clk; + struct clk *ref_clk_parent; + struct clk *tx0_symbol_clk; + struct clk *rx0_symbol_clk; + struct clk *rx1_symbol_clk; + const struct samsung_ufs_phy_drvdata *drvdata; + struct samsung_ufs_phy_cfg **cfg; + const struct pmu_isol *isol; + u8 lane_cnt; + int ufs_phy_state; + enum phy_mode mode; +}; + +static inline struct samsung_ufs_phy *get_samsung_ufs_phy(struct phy *phy) +{ + return (struct samsung_ufs_phy *)phy_get_drvdata(phy); +} + +static inline void samsung_ufs_phy_ctrl_isol( + struct samsung_ufs_phy *phy, u32 isol) +{ + regmap_update_bits(phy->reg_pmu, phy->isol->offset, + phy->isol->mask, isol ? 0 : phy->isol->en); +} + +#include "phy-exynos7-ufs.h" + +#endif /* _PHY_SAMSUNG_UFS_ */ -- cgit v1.2.3-59-g8ed1b From ef19b117b83466e1c030368101a24367a34be7f0 Mon Sep 17 00:00:00 2001 From: Ansuel Smith Date: Fri, 17 Jul 2020 15:16:31 +0200 Subject: phy: qualcomm: add qcom ipq806x dwc usb phy driver This has lost in the original push for the dwc3 qcom driver. This is needed for ipq806x SoC as without this the usb ports doesn't work at all. Signed-off-by: Andy Gross Signed-off-by: Ansuel Smith Tested-by: Jonathan McDowell Link: https://lore.kernel.org/r/20200717131635.11076-1-ansuelsmth@gmail.com Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/Kconfig | 10 + drivers/phy/qualcomm/Makefile | 1 + drivers/phy/qualcomm/phy-qcom-ipq806x-usb.c | 571 ++++++++++++++++++++++++++++ 3 files changed, 582 insertions(+) create mode 100644 drivers/phy/qualcomm/phy-qcom-ipq806x-usb.c diff --git a/drivers/phy/qualcomm/Kconfig b/drivers/phy/qualcomm/Kconfig index e6f0e514fb1f..928db510b86c 100644 --- a/drivers/phy/qualcomm/Kconfig +++ b/drivers/phy/qualcomm/Kconfig @@ -104,3 +104,13 @@ config PHY_QCOM_USB_SS help Enable this to support the Super-Speed USB transceiver on various Qualcomm chipsets. + +config PHY_QCOM_IPQ806X_USB + tristate "Qualcomm IPQ806x DWC3 USB PHY driver" + depends on HAS_IOMEM + depends on OF && (ARCH_QCOM || COMPILE_TEST) + select GENERIC_PHY + help + This option enables support for the Synopsis PHYs present inside the + Qualcomm USB3.0 DWC3 controller on ipq806x SoC. This driver supports + both HS and SS PHY controllers. diff --git a/drivers/phy/qualcomm/Makefile b/drivers/phy/qualcomm/Makefile index a767915decd0..47acbd7daa3a 100644 --- a/drivers/phy/qualcomm/Makefile +++ b/drivers/phy/qualcomm/Makefile @@ -11,3 +11,4 @@ obj-$(CONFIG_PHY_QCOM_USB_HSIC) += phy-qcom-usb-hsic.o obj-$(CONFIG_PHY_QCOM_USB_HS_28NM) += phy-qcom-usb-hs-28nm.o obj-$(CONFIG_PHY_QCOM_USB_SS) += phy-qcom-usb-ss.o obj-$(CONFIG_PHY_QCOM_USB_SNPS_FEMTO_V2)+= phy-qcom-snps-femto-v2.o +obj-$(CONFIG_PHY_QCOM_IPQ806X_USB) += phy-qcom-ipq806x-usb.o diff --git a/drivers/phy/qualcomm/phy-qcom-ipq806x-usb.c b/drivers/phy/qualcomm/phy-qcom-ipq806x-usb.c new file mode 100644 index 000000000000..a7241bf110d7 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-ipq806x-usb.c @@ -0,0 +1,571 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* USB QSCRATCH Hardware registers */ +#define QSCRATCH_GENERAL_CFG (0x08) +#define HSUSB_PHY_CTRL_REG (0x10) + +/* PHY_CTRL_REG */ +#define HSUSB_CTRL_DMSEHV_CLAMP BIT(24) +#define HSUSB_CTRL_USB2_SUSPEND BIT(23) +#define HSUSB_CTRL_UTMI_CLK_EN BIT(21) +#define HSUSB_CTRL_UTMI_OTG_VBUS_VALID BIT(20) +#define HSUSB_CTRL_USE_CLKCORE BIT(18) +#define HSUSB_CTRL_DPSEHV_CLAMP BIT(17) +#define HSUSB_CTRL_COMMONONN BIT(11) +#define HSUSB_CTRL_ID_HV_CLAMP BIT(9) +#define HSUSB_CTRL_OTGSESSVLD_CLAMP BIT(8) +#define HSUSB_CTRL_CLAMP_EN BIT(7) +#define HSUSB_CTRL_RETENABLEN BIT(1) +#define HSUSB_CTRL_POR BIT(0) + +/* QSCRATCH_GENERAL_CFG */ +#define HSUSB_GCFG_XHCI_REV BIT(2) + +/* USB QSCRATCH Hardware registers */ +#define SSUSB_PHY_CTRL_REG (0x00) +#define SSUSB_PHY_PARAM_CTRL_1 (0x04) +#define SSUSB_PHY_PARAM_CTRL_2 (0x08) +#define CR_PROTOCOL_DATA_IN_REG (0x0c) +#define CR_PROTOCOL_DATA_OUT_REG (0x10) +#define CR_PROTOCOL_CAP_ADDR_REG (0x14) +#define CR_PROTOCOL_CAP_DATA_REG (0x18) +#define CR_PROTOCOL_READ_REG (0x1c) +#define CR_PROTOCOL_WRITE_REG (0x20) + +/* PHY_CTRL_REG */ +#define SSUSB_CTRL_REF_USE_PAD BIT(28) +#define SSUSB_CTRL_TEST_POWERDOWN BIT(27) +#define SSUSB_CTRL_LANE0_PWR_PRESENT BIT(24) +#define SSUSB_CTRL_SS_PHY_EN BIT(8) +#define SSUSB_CTRL_SS_PHY_RESET BIT(7) + +/* SSPHY control registers - Does this need 0x30? */ +#define SSPHY_CTRL_RX_OVRD_IN_HI(lane) (0x1006 + 0x100 * (lane)) +#define SSPHY_CTRL_TX_OVRD_DRV_LO(lane) (0x1002 + 0x100 * (lane)) + +/* SSPHY SoC version specific values */ +#define SSPHY_RX_EQ_VALUE 4 /* Override value for rx_eq */ +/* Override value for transmit preemphasis */ +#define SSPHY_TX_DEEMPH_3_5DB 23 +/* Override value for mpll */ +#define SSPHY_MPLL_VALUE 0 + +/* QSCRATCH PHY_PARAM_CTRL1 fields */ +#define PHY_PARAM_CTRL1_TX_FULL_SWING_MASK GENMASK(26, 19) +#define PHY_PARAM_CTRL1_TX_DEEMPH_6DB_MASK GENMASK(19, 13) +#define PHY_PARAM_CTRL1_TX_DEEMPH_3_5DB_MASK GENMASK(13, 7) +#define PHY_PARAM_CTRL1_LOS_BIAS_MASK GENMASK(7, 2) + +#define PHY_PARAM_CTRL1_MASK \ + (PHY_PARAM_CTRL1_TX_FULL_SWING_MASK | \ + PHY_PARAM_CTRL1_TX_DEEMPH_6DB_MASK | \ + PHY_PARAM_CTRL1_TX_DEEMPH_3_5DB_MASK | \ + PHY_PARAM_CTRL1_LOS_BIAS_MASK) + +#define PHY_PARAM_CTRL1_TX_FULL_SWING(x) \ + (((x) << 20) & PHY_PARAM_CTRL1_TX_FULL_SWING_MASK) +#define PHY_PARAM_CTRL1_TX_DEEMPH_6DB(x) \ + (((x) << 14) & PHY_PARAM_CTRL1_TX_DEEMPH_6DB_MASK) +#define PHY_PARAM_CTRL1_TX_DEEMPH_3_5DB(x) \ + (((x) << 8) & PHY_PARAM_CTRL1_TX_DEEMPH_3_5DB_MASK) +#define PHY_PARAM_CTRL1_LOS_BIAS(x) \ + (((x) << 3) & PHY_PARAM_CTRL1_LOS_BIAS_MASK) + +/* RX OVRD IN HI bits */ +#define RX_OVRD_IN_HI_RX_RESET_OVRD BIT(13) +#define RX_OVRD_IN_HI_RX_RX_RESET BIT(12) +#define RX_OVRD_IN_HI_RX_EQ_OVRD BIT(11) +#define RX_OVRD_IN_HI_RX_EQ_MASK GENMASK(10, 7) +#define RX_OVRD_IN_HI_RX_EQ(x) ((x) << 8) +#define RX_OVRD_IN_HI_RX_EQ_EN_OVRD BIT(7) +#define RX_OVRD_IN_HI_RX_EQ_EN BIT(6) +#define RX_OVRD_IN_HI_RX_LOS_FILTER_OVRD BIT(5) +#define RX_OVRD_IN_HI_RX_LOS_FILTER_MASK GENMASK(4, 2) +#define RX_OVRD_IN_HI_RX_RATE_OVRD BIT(2) +#define RX_OVRD_IN_HI_RX_RATE_MASK GENMASK(2, 0) + +/* TX OVRD DRV LO register bits */ +#define TX_OVRD_DRV_LO_AMPLITUDE_MASK GENMASK(6, 0) +#define TX_OVRD_DRV_LO_PREEMPH_MASK GENMASK(13, 6) +#define TX_OVRD_DRV_LO_PREEMPH(x) ((x) << 7) +#define TX_OVRD_DRV_LO_EN BIT(14) + +/* MPLL bits */ +#define SSPHY_MPLL_MASK GENMASK(8, 5) +#define SSPHY_MPLL(x) ((x) << 5) + +/* SS CAP register bits */ +#define SS_CR_CAP_ADDR_REG BIT(0) +#define SS_CR_CAP_DATA_REG BIT(0) +#define SS_CR_READ_REG BIT(0) +#define SS_CR_WRITE_REG BIT(0) + +struct usb_phy { + void __iomem *base; + struct device *dev; + struct clk *xo_clk; + struct clk *ref_clk; + u32 rx_eq; + u32 tx_deamp_3_5db; + u32 mpll; +}; + +struct phy_drvdata { + struct phy_ops ops; + u32 clk_rate; +}; + +/** + * Write register and read back masked value to confirm it is written + * + * @base - QCOM DWC3 PHY base virtual address. + * @offset - register offset. + * @mask - register bitmask specifying what should be updated + * @val - value to write. + */ +static inline void usb_phy_write_readback(struct usb_phy *phy_dwc3, + u32 offset, + const u32 mask, u32 val) +{ + u32 write_val, tmp = readl(phy_dwc3->base + offset); + + tmp &= ~mask; /* retain other bits */ + write_val = tmp | val; + + writel(write_val, phy_dwc3->base + offset); + + /* Read back to see if val was written */ + tmp = readl(phy_dwc3->base + offset); + tmp &= mask; /* clear other bits */ + + if (tmp != val) + dev_err(phy_dwc3->dev, "write: %x to QSCRATCH: %x FAILED\n", val, offset); +} + +static int wait_for_latch(void __iomem *addr) +{ + u32 retry = 10; + + while (true) { + if (!readl(addr)) + break; + + if (--retry == 0) + return -ETIMEDOUT; + + usleep_range(10, 20); + } + + return 0; +} + +/** + * Write SSPHY register + * + * @base - QCOM DWC3 PHY base virtual address. + * @addr - SSPHY address to write. + * @val - value to write. + */ +static int usb_ss_write_phycreg(struct usb_phy *phy_dwc3, + u32 addr, u32 val) +{ + int ret; + + writel(addr, phy_dwc3->base + CR_PROTOCOL_DATA_IN_REG); + writel(SS_CR_CAP_ADDR_REG, + phy_dwc3->base + CR_PROTOCOL_CAP_ADDR_REG); + + ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_CAP_ADDR_REG); + if (ret) + goto err_wait; + + writel(val, phy_dwc3->base + CR_PROTOCOL_DATA_IN_REG); + writel(SS_CR_CAP_DATA_REG, + phy_dwc3->base + CR_PROTOCOL_CAP_DATA_REG); + + ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_CAP_DATA_REG); + if (ret) + goto err_wait; + + writel(SS_CR_WRITE_REG, phy_dwc3->base + CR_PROTOCOL_WRITE_REG); + + ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_WRITE_REG); + +err_wait: + if (ret) + dev_err(phy_dwc3->dev, "timeout waiting for latch\n"); + return ret; +} + +/** + * Read SSPHY register. + * + * @base - QCOM DWC3 PHY base virtual address. + * @addr - SSPHY address to read. + */ +static int usb_ss_read_phycreg(struct usb_phy *phy_dwc3, + u32 addr, u32 *val) +{ + int ret; + + writel(addr, phy_dwc3->base + CR_PROTOCOL_DATA_IN_REG); + writel(SS_CR_CAP_ADDR_REG, + phy_dwc3->base + CR_PROTOCOL_CAP_ADDR_REG); + + ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_CAP_ADDR_REG); + if (ret) + goto err_wait; + + /* + * Due to hardware bug, first read of SSPHY register might be + * incorrect. Hence as workaround, SW should perform SSPHY register + * read twice, but use only second read and ignore first read. + */ + writel(SS_CR_READ_REG, phy_dwc3->base + CR_PROTOCOL_READ_REG); + + ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_READ_REG); + if (ret) + goto err_wait; + + /* throwaway read */ + readl(phy_dwc3->base + CR_PROTOCOL_DATA_OUT_REG); + + writel(SS_CR_READ_REG, phy_dwc3->base + CR_PROTOCOL_READ_REG); + + ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_READ_REG); + if (ret) + goto err_wait; + + *val = readl(phy_dwc3->base + CR_PROTOCOL_DATA_OUT_REG); + +err_wait: + return ret; +} + +static int qcom_ipq806x_usb_hs_phy_init(struct phy *phy) +{ + struct usb_phy *phy_dwc3 = phy_get_drvdata(phy); + int ret; + u32 val; + + ret = clk_prepare_enable(phy_dwc3->xo_clk); + if (ret) + return ret; + + ret = clk_prepare_enable(phy_dwc3->ref_clk); + if (ret) { + clk_disable_unprepare(phy_dwc3->xo_clk); + return ret; + } + + /* + * HSPHY Initialization: Enable UTMI clock, select 19.2MHz fsel + * enable clamping, and disable RETENTION (power-on default is ENABLED) + */ + val = HSUSB_CTRL_DPSEHV_CLAMP | HSUSB_CTRL_DMSEHV_CLAMP | + HSUSB_CTRL_RETENABLEN | HSUSB_CTRL_COMMONONN | + HSUSB_CTRL_OTGSESSVLD_CLAMP | HSUSB_CTRL_ID_HV_CLAMP | + HSUSB_CTRL_DPSEHV_CLAMP | HSUSB_CTRL_UTMI_OTG_VBUS_VALID | + HSUSB_CTRL_UTMI_CLK_EN | HSUSB_CTRL_CLAMP_EN | 0x70; + + /* use core clock if external reference is not present */ + if (!phy_dwc3->xo_clk) + val |= HSUSB_CTRL_USE_CLKCORE; + + writel(val, phy_dwc3->base + HSUSB_PHY_CTRL_REG); + usleep_range(2000, 2200); + + /* Disable (bypass) VBUS and ID filters */ + writel(HSUSB_GCFG_XHCI_REV, phy_dwc3->base + QSCRATCH_GENERAL_CFG); + + return 0; +} + +static int qcom_ipq806x_usb_hs_phy_exit(struct phy *phy) +{ + struct usb_phy *phy_dwc3 = phy_get_drvdata(phy); + + clk_disable_unprepare(phy_dwc3->ref_clk); + clk_disable_unprepare(phy_dwc3->xo_clk); + + return 0; +} + +static int qcom_ipq806x_usb_ss_phy_init(struct phy *phy) +{ + struct usb_phy *phy_dwc3 = phy_get_drvdata(phy); + int ret; + u32 data; + + ret = clk_prepare_enable(phy_dwc3->xo_clk); + if (ret) + return ret; + + ret = clk_prepare_enable(phy_dwc3->ref_clk); + if (ret) { + clk_disable_unprepare(phy_dwc3->xo_clk); + return ret; + } + + /* reset phy */ + data = readl(phy_dwc3->base + SSUSB_PHY_CTRL_REG); + writel(data | SSUSB_CTRL_SS_PHY_RESET, + phy_dwc3->base + SSUSB_PHY_CTRL_REG); + usleep_range(2000, 2200); + writel(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG); + + /* clear REF_PAD if we don't have XO clk */ + if (!phy_dwc3->xo_clk) + data &= ~SSUSB_CTRL_REF_USE_PAD; + else + data |= SSUSB_CTRL_REF_USE_PAD; + + writel(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG); + + /* wait for ref clk to become stable, this can take up to 30ms */ + msleep(30); + + data |= SSUSB_CTRL_SS_PHY_EN | SSUSB_CTRL_LANE0_PWR_PRESENT; + writel(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG); + + /* + * WORKAROUND: There is SSPHY suspend bug due to which USB enumerates + * in HS mode instead of SS mode. Workaround it by asserting + * LANE0.TX_ALT_BLOCK.EN_ALT_BUS to enable TX to use alt bus mode + */ + ret = usb_ss_read_phycreg(phy_dwc3, 0x102D, &data); + if (ret) + goto err_phy_trans; + + data |= (1 << 7); + ret = usb_ss_write_phycreg(phy_dwc3, 0x102D, data); + if (ret) + goto err_phy_trans; + + ret = usb_ss_read_phycreg(phy_dwc3, 0x1010, &data); + if (ret) + goto err_phy_trans; + + data &= ~0xff0; + data |= 0x20; + ret = usb_ss_write_phycreg(phy_dwc3, 0x1010, data); + if (ret) + goto err_phy_trans; + + /* + * Fix RX Equalization setting as follows + * LANE0.RX_OVRD_IN_HI. RX_EQ_EN set to 0 + * LANE0.RX_OVRD_IN_HI.RX_EQ_EN_OVRD set to 1 + * LANE0.RX_OVRD_IN_HI.RX_EQ set based on SoC version + * LANE0.RX_OVRD_IN_HI.RX_EQ_OVRD set to 1 + */ + ret = usb_ss_read_phycreg(phy_dwc3, SSPHY_CTRL_RX_OVRD_IN_HI(0), &data); + if (ret) + goto err_phy_trans; + + data &= ~RX_OVRD_IN_HI_RX_EQ_EN; + data |= RX_OVRD_IN_HI_RX_EQ_EN_OVRD; + data &= ~RX_OVRD_IN_HI_RX_EQ_MASK; + data |= RX_OVRD_IN_HI_RX_EQ(phy_dwc3->rx_eq); + data |= RX_OVRD_IN_HI_RX_EQ_OVRD; + ret = usb_ss_write_phycreg(phy_dwc3, + SSPHY_CTRL_RX_OVRD_IN_HI(0), data); + if (ret) + goto err_phy_trans; + + /* + * Set EQ and TX launch amplitudes as follows + * LANE0.TX_OVRD_DRV_LO.PREEMPH set based on SoC version + * LANE0.TX_OVRD_DRV_LO.AMPLITUDE set to 110 + * LANE0.TX_OVRD_DRV_LO.EN set to 1. + */ + ret = usb_ss_read_phycreg(phy_dwc3, + SSPHY_CTRL_TX_OVRD_DRV_LO(0), &data); + if (ret) + goto err_phy_trans; + + data &= ~TX_OVRD_DRV_LO_PREEMPH_MASK; + data |= TX_OVRD_DRV_LO_PREEMPH(phy_dwc3->tx_deamp_3_5db); + data &= ~TX_OVRD_DRV_LO_AMPLITUDE_MASK; + data |= 0x6E; + data |= TX_OVRD_DRV_LO_EN; + ret = usb_ss_write_phycreg(phy_dwc3, + SSPHY_CTRL_TX_OVRD_DRV_LO(0), data); + if (ret) + goto err_phy_trans; + + data = 0; + data &= ~SSPHY_MPLL_MASK; + data |= SSPHY_MPLL(phy_dwc3->mpll); + usb_ss_write_phycreg(phy_dwc3, 0x30, data); + + /* + * Set the QSCRATCH PHY_PARAM_CTRL1 parameters as follows + * TX_FULL_SWING [26:20] amplitude to 110 + * TX_DEEMPH_6DB [19:14] to 32 + * TX_DEEMPH_3_5DB [13:8] set based on SoC version + * LOS_BIAS [7:3] to 9 + */ + data = readl(phy_dwc3->base + SSUSB_PHY_PARAM_CTRL_1); + + data &= ~PHY_PARAM_CTRL1_MASK; + + data |= PHY_PARAM_CTRL1_TX_FULL_SWING(0x6e) | + PHY_PARAM_CTRL1_TX_DEEMPH_6DB(0x20) | + PHY_PARAM_CTRL1_TX_DEEMPH_3_5DB(phy_dwc3->tx_deamp_3_5db) | + PHY_PARAM_CTRL1_LOS_BIAS(0x9); + + usb_phy_write_readback(phy_dwc3, SSUSB_PHY_PARAM_CTRL_1, + PHY_PARAM_CTRL1_MASK, data); + +err_phy_trans: + return ret; +} + +static int qcom_ipq806x_usb_ss_phy_exit(struct phy *phy) +{ + struct usb_phy *phy_dwc3 = phy_get_drvdata(phy); + + /* Sequence to put SSPHY in low power state: + * 1. Clear REF_PHY_EN in PHY_CTRL_REG + * 2. Clear REF_USE_PAD in PHY_CTRL_REG + * 3. Set TEST_POWERED_DOWN in PHY_CTRL_REG to enable PHY retention + */ + usb_phy_write_readback(phy_dwc3, SSUSB_PHY_CTRL_REG, + SSUSB_CTRL_SS_PHY_EN, 0x0); + usb_phy_write_readback(phy_dwc3, SSUSB_PHY_CTRL_REG, + SSUSB_CTRL_REF_USE_PAD, 0x0); + usb_phy_write_readback(phy_dwc3, SSUSB_PHY_CTRL_REG, + SSUSB_CTRL_TEST_POWERDOWN, 0x0); + + clk_disable_unprepare(phy_dwc3->ref_clk); + clk_disable_unprepare(phy_dwc3->xo_clk); + + return 0; +} + +static const struct phy_drvdata qcom_ipq806x_usb_hs_drvdata = { + .ops = { + .init = qcom_ipq806x_usb_hs_phy_init, + .exit = qcom_ipq806x_usb_hs_phy_exit, + .owner = THIS_MODULE, + }, + .clk_rate = 60000000, +}; + +static const struct phy_drvdata qcom_ipq806x_usb_ss_drvdata = { + .ops = { + .init = qcom_ipq806x_usb_ss_phy_init, + .exit = qcom_ipq806x_usb_ss_phy_exit, + .owner = THIS_MODULE, + }, + .clk_rate = 125000000, +}; + +static const struct of_device_id qcom_ipq806x_usb_phy_table[] = { + { .compatible = "qcom,ipq806x-usb-phy-hs", + .data = &qcom_ipq806x_usb_hs_drvdata }, + { .compatible = "qcom,ipq806x-usb-phy-ss", + .data = &qcom_ipq806x_usb_ss_drvdata }, + { /* Sentinel */ } +}; +MODULE_DEVICE_TABLE(of, qcom_ipq806x_usb_phy_table); + +static int qcom_ipq806x_usb_phy_probe(struct platform_device *pdev) +{ + struct resource *res; + resource_size_t size; + struct phy *generic_phy; + struct usb_phy *phy_dwc3; + const struct phy_drvdata *data; + struct phy_provider *phy_provider; + + phy_dwc3 = devm_kzalloc(&pdev->dev, sizeof(*phy_dwc3), GFP_KERNEL); + if (!phy_dwc3) + return -ENOMEM; + + data = of_device_get_match_data(&pdev->dev); + + phy_dwc3->dev = &pdev->dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -EINVAL; + size = resource_size(res); + phy_dwc3->base = devm_ioremap(phy_dwc3->dev, res->start, size); + + if (IS_ERR(phy_dwc3->base)) { + dev_err(phy_dwc3->dev, "failed to map reg\n"); + return PTR_ERR(phy_dwc3->base); + } + + phy_dwc3->ref_clk = devm_clk_get(phy_dwc3->dev, "ref"); + if (IS_ERR(phy_dwc3->ref_clk)) { + dev_dbg(phy_dwc3->dev, "cannot get reference clock\n"); + return PTR_ERR(phy_dwc3->ref_clk); + } + + clk_set_rate(phy_dwc3->ref_clk, data->clk_rate); + + phy_dwc3->xo_clk = devm_clk_get(phy_dwc3->dev, "xo"); + if (IS_ERR(phy_dwc3->xo_clk)) { + dev_dbg(phy_dwc3->dev, "cannot get TCXO clock\n"); + phy_dwc3->xo_clk = NULL; + } + + /* Parse device node to probe HSIO settings */ + if (device_property_read_u32(&pdev->dev, "qcom,rx-eq", + &phy_dwc3->rx_eq)) + phy_dwc3->rx_eq = SSPHY_RX_EQ_VALUE; + + if (device_property_read_u32(&pdev->dev, "qcom,tx-deamp_3_5db", + &phy_dwc3->tx_deamp_3_5db)) + phy_dwc3->rx_eq = SSPHY_TX_DEEMPH_3_5DB; + + if (device_property_read_u32(&pdev->dev, "qcom,mpll", &phy_dwc3->mpll)) + phy_dwc3->mpll = SSPHY_MPLL_VALUE; + + generic_phy = devm_phy_create(phy_dwc3->dev, pdev->dev.of_node, &data->ops); + + if (IS_ERR(generic_phy)) + return PTR_ERR(generic_phy); + + phy_set_drvdata(generic_phy, phy_dwc3); + platform_set_drvdata(pdev, phy_dwc3); + + phy_provider = devm_of_phy_provider_register(phy_dwc3->dev, + of_phy_simple_xlate); + + if (IS_ERR(phy_provider)) + return PTR_ERR(phy_provider); + + return 0; +} + +static struct platform_driver qcom_ipq806x_usb_phy_driver = { + .probe = qcom_ipq806x_usb_phy_probe, + .driver = { + .name = "qcom-ipq806x-usb-phy", + .owner = THIS_MODULE, + .of_match_table = qcom_ipq806x_usb_phy_table, + }, +}; + +module_platform_driver(qcom_ipq806x_usb_phy_driver); + +MODULE_ALIAS("platform:phy-qcom-ipq806x-usb"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Andy Gross "); +MODULE_AUTHOR("Ivan T. Ivanov "); +MODULE_DESCRIPTION("DesignWare USB3 QCOM PHY driver"); -- cgit v1.2.3-59-g8ed1b From dc171790c729db72774fd076485b86f92954544b Mon Sep 17 00:00:00 2001 From: Ansuel Smith Date: Fri, 17 Jul 2020 15:16:32 +0200 Subject: devicetree: bindings: phy: Document ipq806x dwc3 qcom phy Document dwc3 qcom phy hs and ss phy bindings needed to correctly inizialize and use usb on ipq806x SoC. Signed-off-by: Ansuel Smith Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20200717131635.11076-2-ansuelsmth@gmail.com Signed-off-by: Vinod Koul --- .../bindings/phy/qcom,ipq806x-usb-phy-hs.yaml | 55 ++++++++++++++++ .../bindings/phy/qcom,ipq806x-usb-phy-ss.yaml | 73 ++++++++++++++++++++++ 2 files changed, 128 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/qcom,ipq806x-usb-phy-hs.yaml create mode 100644 Documentation/devicetree/bindings/phy/qcom,ipq806x-usb-phy-ss.yaml diff --git a/Documentation/devicetree/bindings/phy/qcom,ipq806x-usb-phy-hs.yaml b/Documentation/devicetree/bindings/phy/qcom,ipq806x-usb-phy-hs.yaml new file mode 100644 index 000000000000..23887ebe08fd --- /dev/null +++ b/Documentation/devicetree/bindings/phy/qcom,ipq806x-usb-phy-hs.yaml @@ -0,0 +1,55 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/qcom,ipq806x-usb-phy-hs.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm ipq806x usb DWC3 HS PHY CONTROLLER + +maintainers: + - Ansuel Smith + +description: + DWC3 PHY nodes are defined to describe on-chip Synopsis Physical layer + controllers used in ipq806x. Each DWC3 PHY controller should have its + own node. + +properties: + compatible: + const: qcom,ipq806x-usb-phy-hs + + "#phy-cells": + const: 0 + + reg: + maxItems: 1 + + clocks: + minItems: 1 + maxItems: 2 + + clock-names: + minItems: 1 + maxItems: 2 + items: + - const: ref + - const: xo + +required: + - compatible + - "#phy-cells" + - reg + - clocks + - clock-names + +examples: + - | + #include + + hs_phy_0: phy@110f8800 { + compatible = "qcom,ipq806x-usb-phy-hs"; + reg = <0x110f8800 0x30>; + clocks = <&gcc USB30_0_UTMI_CLK>; + clock-names = "ref"; + #phy-cells = <0>; + }; diff --git a/Documentation/devicetree/bindings/phy/qcom,ipq806x-usb-phy-ss.yaml b/Documentation/devicetree/bindings/phy/qcom,ipq806x-usb-phy-ss.yaml new file mode 100644 index 000000000000..fa30c24b4405 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/qcom,ipq806x-usb-phy-ss.yaml @@ -0,0 +1,73 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/qcom,ipq806x-usb-phy-ss.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm ipq806x usb DWC3 SS PHY CONTROLLER + +maintainers: + - Ansuel Smith + +description: + DWC3 PHY nodes are defined to describe on-chip Synopsis Physical layer + controllers used in ipq806x. Each DWC3 PHY controller should have its + own node. + +properties: + compatible: + const: qcom,ipq806x-usb-phy-ss + + "#phy-cells": + const: 0 + + reg: + maxItems: 1 + + clocks: + minItems: 1 + maxItems: 2 + + clock-names: + minItems: 1 + maxItems: 2 + items: + - const: ref + - const: xo + + qcom,rx-eq: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Override value for rx_eq. + default: 4 + maximum: 7 + + qcom,tx-deamp-3_5db: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Override value for transmit preemphasis. + default: 23 + maximum: 63 + + qcom,mpll: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Override value for mpll. + default: 0 + maximum: 7 + +required: + - compatible + - "#phy-cells" + - reg + - clocks + - clock-names + +examples: + - | + #include + + ss_phy_0: phy@110f8830 { + compatible = "qcom,ipq806x-usb-phy-ss"; + reg = <0x110f8830 0x30>; + clocks = <&gcc USB30_0_MASTER_CLK>; + clock-names = "ref"; + #phy-cells = <0>; + }; -- cgit v1.2.3-59-g8ed1b From 08b0ad375ca66181faee725b1b358bcae8d592ee Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Fri, 17 Jul 2020 20:44:56 +0900 Subject: phy: renesas: rcar-gen3-usb2: move irq registration to init If CONFIG_DEBUG_SHIRQ was enabled, r8a77951-salvator-xs could boot correctly. If we appended "earlycon keep_bootcon" to the kernel command like, we could get kernel log like below. SError Interrupt on CPU0, code 0xbf000002 -- SError CPU: 0 PID: 1 Comm: swapper/0 Not tainted 5.8.0-rc3-salvator-x-00505-g6c843129e6faaf01 #785 Hardware name: Renesas Salvator-X 2nd version board based on r8a77951 (DT) pstate: 60400085 (nZCv daIf +PAN -UAO BTYPE=--) pc : rcar_gen3_phy_usb2_irq+0x14/0x54 lr : free_irq+0xf4/0x27c This means free_irq() calls the interrupt handler while PM runtime is not getting if DEBUG_SHIRQ is enabled and rcar_gen3_phy_usb2_probe() failed. To fix the issue, move the irq registration place to rcar_gen3_phy_usb2_init() which is ready to handle the interrupts. Note that after the commit 549b6b55b005 ("phy: renesas: rcar-gen3-usb2: enable/disable independent irqs") which is merged into v5.2, since this driver creates multiple phy instances, needs to check whether one of phy instances is initialized. However, if we backport this patch to v5.1 or less, we don't need to check it because such kernel have single phy instance. Reported-by: Wolfram Sang Reported-by: Geert Uytterhoeven Fixes: 9f391c574efc ("phy: rcar-gen3-usb2: add runtime ID/VBUS pin detection") Signed-off-by: Yoshihiro Shimoda Link: https://lore.kernel.org/r/1594986297-12434-2-git-send-email-yoshihiro.shimoda.uh@renesas.com Signed-off-by: Vinod Koul --- drivers/phy/renesas/phy-rcar-gen3-usb2.c | 61 +++++++++++++++++--------------- 1 file changed, 33 insertions(+), 28 deletions(-) diff --git a/drivers/phy/renesas/phy-rcar-gen3-usb2.c b/drivers/phy/renesas/phy-rcar-gen3-usb2.c index bfb22f868857..5087b7c44d55 100644 --- a/drivers/phy/renesas/phy-rcar-gen3-usb2.c +++ b/drivers/phy/renesas/phy-rcar-gen3-usb2.c @@ -111,6 +111,7 @@ struct rcar_gen3_chan { struct work_struct work; struct mutex lock; /* protects rphys[...].powered */ enum usb_dr_mode dr_mode; + int irq; bool extcon_host; bool is_otg_channel; bool uses_otg_pins; @@ -389,12 +390,38 @@ static void rcar_gen3_init_otg(struct rcar_gen3_chan *ch) rcar_gen3_device_recognition(ch); } +static irqreturn_t rcar_gen3_phy_usb2_irq(int irq, void *_ch) +{ + struct rcar_gen3_chan *ch = _ch; + void __iomem *usb2_base = ch->base; + u32 status = readl(usb2_base + USB2_OBINTSTA); + irqreturn_t ret = IRQ_NONE; + + if (status & USB2_OBINT_BITS) { + dev_vdbg(ch->dev, "%s: %08x\n", __func__, status); + writel(USB2_OBINT_BITS, usb2_base + USB2_OBINTSTA); + rcar_gen3_device_recognition(ch); + ret = IRQ_HANDLED; + } + + return ret; +} + static int rcar_gen3_phy_usb2_init(struct phy *p) { struct rcar_gen3_phy *rphy = phy_get_drvdata(p); struct rcar_gen3_chan *channel = rphy->ch; void __iomem *usb2_base = channel->base; u32 val; + int ret; + + if (!rcar_gen3_is_any_rphy_initialized(channel) && channel->irq >= 0) { + INIT_WORK(&channel->work, rcar_gen3_phy_usb2_work); + ret = request_irq(channel->irq, rcar_gen3_phy_usb2_irq, + IRQF_SHARED, dev_name(channel->dev), channel); + if (ret < 0) + dev_err(channel->dev, "No irq handler (%d)\n", channel->irq); + } /* Initialize USB2 part */ val = readl(usb2_base + USB2_INT_ENABLE); @@ -433,6 +460,9 @@ static int rcar_gen3_phy_usb2_exit(struct phy *p) val &= ~USB2_INT_ENABLE_UCOM_INTEN; writel(val, usb2_base + USB2_INT_ENABLE); + if (channel->irq >= 0 && !rcar_gen3_is_any_rphy_initialized(channel)) + free_irq(channel->irq, channel); + return 0; } @@ -503,23 +533,6 @@ static const struct phy_ops rz_g1c_phy_usb2_ops = { .owner = THIS_MODULE, }; -static irqreturn_t rcar_gen3_phy_usb2_irq(int irq, void *_ch) -{ - struct rcar_gen3_chan *ch = _ch; - void __iomem *usb2_base = ch->base; - u32 status = readl(usb2_base + USB2_OBINTSTA); - irqreturn_t ret = IRQ_NONE; - - if (status & USB2_OBINT_BITS) { - dev_vdbg(ch->dev, "%s: %08x\n", __func__, status); - writel(USB2_OBINT_BITS, usb2_base + USB2_OBINTSTA); - rcar_gen3_device_recognition(ch); - ret = IRQ_HANDLED; - } - - return ret; -} - static const struct of_device_id rcar_gen3_phy_usb2_match_table[] = { { .compatible = "renesas,usb2-phy-r8a77470", @@ -598,7 +611,7 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev) struct phy_provider *provider; struct resource *res; const struct phy_ops *phy_usb2_ops; - int irq, ret = 0, i; + int ret = 0, i; if (!dev->of_node) { dev_err(dev, "This driver needs device tree\n"); @@ -614,16 +627,8 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev) if (IS_ERR(channel->base)) return PTR_ERR(channel->base); - /* call request_irq for OTG */ - irq = platform_get_irq_optional(pdev, 0); - if (irq >= 0) { - INIT_WORK(&channel->work, rcar_gen3_phy_usb2_work); - irq = devm_request_irq(dev, irq, rcar_gen3_phy_usb2_irq, - IRQF_SHARED, dev_name(dev), channel); - if (irq < 0) - dev_err(dev, "No irq handler (%d)\n", irq); - } - + /* get irq number here and request_irq for OTG in phy_init */ + channel->irq = platform_get_irq_optional(pdev, 0); channel->dr_mode = rcar_gen3_get_dr_mode(dev->of_node); if (channel->dr_mode != USB_DR_MODE_UNKNOWN) { int ret; -- cgit v1.2.3-59-g8ed1b From b59aeb1a59ccece01f4f0c675d18cfac9cc825fb Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Fri, 17 Jul 2020 20:44:57 +0900 Subject: phy: renesas: rcar-gen3-usb2: exit if request_irq() failed To avoid unexpected behaviors, it's better to exit if request_irq() failed. Suggested-by: Vinod Koul Signed-off-by: Yoshihiro Shimoda Link: https://lore.kernel.org/r/1594986297-12434-3-git-send-email-yoshihiro.shimoda.uh@renesas.com Signed-off-by: Vinod Koul --- drivers/phy/renesas/phy-rcar-gen3-usb2.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/phy/renesas/phy-rcar-gen3-usb2.c b/drivers/phy/renesas/phy-rcar-gen3-usb2.c index 5087b7c44d55..e34e4475027c 100644 --- a/drivers/phy/renesas/phy-rcar-gen3-usb2.c +++ b/drivers/phy/renesas/phy-rcar-gen3-usb2.c @@ -419,8 +419,10 @@ static int rcar_gen3_phy_usb2_init(struct phy *p) INIT_WORK(&channel->work, rcar_gen3_phy_usb2_work); ret = request_irq(channel->irq, rcar_gen3_phy_usb2_irq, IRQF_SHARED, dev_name(channel->dev), channel); - if (ret < 0) + if (ret < 0) { dev_err(channel->dev, "No irq handler (%d)\n", channel->irq); + return ret; + } } /* Initialize USB2 part */ -- cgit v1.2.3-59-g8ed1b From f199d6a296dce05ba3de40865251320371e68e3d Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Thu, 16 Jul 2020 18:18:20 +0100 Subject: dt-bindings: phy: renesas,usb2-phy: Add r8a774e1 support Document SoC specific bindings for RZ/G2H (r8a774e1) SoC. Signed-off-by: Lad Prabhakar Reviewed-by: Marian-Cristian Rotariu Link: https://lore.kernel.org/r/1594919915-5225-6-git-send-email-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/phy/renesas,usb2-phy.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/phy/renesas,usb2-phy.yaml b/Documentation/devicetree/bindings/phy/renesas,usb2-phy.yaml index 440f09fddf93..829e8c7e467a 100644 --- a/Documentation/devicetree/bindings/phy/renesas,usb2-phy.yaml +++ b/Documentation/devicetree/bindings/phy/renesas,usb2-phy.yaml @@ -21,6 +21,7 @@ properties: - renesas,usb2-phy-r8a774a1 # RZ/G2M - renesas,usb2-phy-r8a774b1 # RZ/G2N - renesas,usb2-phy-r8a774c0 # RZ/G2E + - renesas,usb2-phy-r8a774e1 # RZ/G2H - renesas,usb2-phy-r8a7795 # R-Car H3 - renesas,usb2-phy-r8a7796 # R-Car M3-W - renesas,usb2-phy-r8a77961 # R-Car M3-W+ -- cgit v1.2.3-59-g8ed1b From f13b89900d364ebb72ec2011dd0cf61b7c3bb3d5 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Thu, 16 Jul 2020 18:18:24 +0100 Subject: dt-bindings: phy: renesas,usb3-phy: Add r8a774e1 support Document RZ/G2H (R8A774E1) SoC bindings. Signed-off-by: Lad Prabhakar Reviewed-by: Marian-Cristian Rotariu Link: https://lore.kernel.org/r/1594919915-5225-10-git-send-email-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/phy/renesas,usb3-phy.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/phy/renesas,usb3-phy.yaml b/Documentation/devicetree/bindings/phy/renesas,usb3-phy.yaml index 68cf9dd0390d..f3ef738a3ff6 100644 --- a/Documentation/devicetree/bindings/phy/renesas,usb3-phy.yaml +++ b/Documentation/devicetree/bindings/phy/renesas,usb3-phy.yaml @@ -15,6 +15,7 @@ properties: - enum: - renesas,r8a774a1-usb3-phy # RZ/G2M - renesas,r8a774b1-usb3-phy # RZ/G2N + - renesas,r8a774e1-usb3-phy # RZ/G2H - renesas,r8a7795-usb3-phy # R-Car H3 - renesas,r8a7796-usb3-phy # R-Car M3-W - renesas,r8a77961-usb3-phy # R-Car M3-W+ -- cgit v1.2.3-59-g8ed1b From 7d2845d58d257921f771f3fbed5982464ee4855b Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 16 Jul 2020 23:09:39 +0800 Subject: soundwire: intel: reuse code for wait loops to set/clear bits Refactor code and use same routines on set/clear Signed-off-by: Pierre-Louis Bossart Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20200716150947.22119-2-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/intel.c | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 7a65414e5714..8c7ae07c0fe1 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -123,40 +123,33 @@ static inline void intel_writew(void __iomem *base, int offset, u16 value) writew(value, base + offset); } -static int intel_clear_bit(void __iomem *base, int offset, u32 value, u32 mask) +static int intel_wait_bit(void __iomem *base, int offset, u32 mask, u32 target) { int timeout = 10; u32 reg_read; - writel(value, base + offset); do { reg_read = readl(base + offset); - if (!(reg_read & mask)) + if ((reg_read & mask) == target) return 0; timeout--; - udelay(50); + usleep_range(50, 100); } while (timeout != 0); return -EAGAIN; } -static int intel_set_bit(void __iomem *base, int offset, u32 value, u32 mask) +static int intel_clear_bit(void __iomem *base, int offset, u32 value, u32 mask) { - int timeout = 10; - u32 reg_read; - writel(value, base + offset); - do { - reg_read = readl(base + offset); - if (reg_read & mask) - return 0; - - timeout--; - udelay(50); - } while (timeout != 0); + return intel_wait_bit(base, offset, mask, 0); +} - return -EAGAIN; +static int intel_set_bit(void __iomem *base, int offset, u32 value, u32 mask) +{ + writel(value, base + offset); + return intel_wait_bit(base, offset, mask, mask); } /* -- cgit v1.2.3-59-g8ed1b From 4a17c441c7cb06ac1e5fd3409a64d8ce78151983 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 16 Jul 2020 23:09:40 +0800 Subject: soundwire: intel: revisit SHIM programming sequences. Somehow the existing code is not aligned with the steps described in the documentation, refactor code and make sure the register programming sequences are correct. Also add missing power-up, power-down and wake capabilities (the last two are used in follow-up patches but introduced here for consistency). Some of the SHIM registers exposed fields that are link specific, and in addition some of the power-related registers (SPA/CPA) take time to be updated. Uncontrolled access leads to timeouts or errors. Add a mutex, shared by all links, so that all accesses to such registers are serialized, and follow a pattern of read-modify-write. This includes making sure SHIM_SYNC is programmed only once, before the first master is powered on. We use a 'shim_mask' field, shared between all links and protected by a mutex, to deal with power-up and power-down sequences. Note that the SYNCPRD value is tied only to the XTAL value and not the current bus frequency or the frame rate. BugLink: https://github.com/thesofproject/linux/issues/1555 Signed-off-by: Rander Wang Signed-off-by: Pierre-Louis Bossart Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20200716150947.22119-3-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/intel.c | 237 ++++++++++++++++++++++++++++++------ drivers/soundwire/intel.h | 4 + drivers/soundwire/intel_init.c | 4 + include/linux/soundwire/sdw_intel.h | 2 + 4 files changed, 213 insertions(+), 34 deletions(-) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 8c7ae07c0fe1..4792613e8e5a 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -46,7 +46,8 @@ #define SDW_SHIM_LCTL_SPA BIT(0) #define SDW_SHIM_LCTL_CPA BIT(8) -#define SDW_SHIM_SYNC_SYNCPRD_VAL 0x176F +#define SDW_SHIM_SYNC_SYNCPRD_VAL_24 (24000 / SDW_CADENCE_GSYNC_KHZ - 1) +#define SDW_SHIM_SYNC_SYNCPRD_VAL_38_4 (38400 / SDW_CADENCE_GSYNC_KHZ - 1) #define SDW_SHIM_SYNC_SYNCPRD GENMASK(14, 0) #define SDW_SHIM_SYNC_SYNCCPU BIT(15) #define SDW_SHIM_SYNC_CMDSYNC_MASK GENMASK(19, 16) @@ -272,8 +273,46 @@ static int intel_link_power_up(struct sdw_intel *sdw) { unsigned int link_id = sdw->instance; void __iomem *shim = sdw->link_res->shim; + u32 *shim_mask = sdw->link_res->shim_mask; + struct sdw_bus *bus = &sdw->cdns.bus; + struct sdw_master_prop *prop = &bus->prop; int spa_mask, cpa_mask; - int link_control, ret; + int link_control; + int ret = 0; + u32 syncprd; + u32 sync_reg; + + mutex_lock(sdw->link_res->shim_lock); + + /* + * The hardware relies on an internal counter, typically 4kHz, + * to generate the SoundWire SSP - which defines a 'safe' + * synchronization point between commands and audio transport + * and allows for multi link synchronization. The SYNCPRD value + * is only dependent on the oscillator clock provided to + * the IP, so adjust based on _DSD properties reported in DSDT + * tables. The values reported are based on either 24MHz + * (CNL/CML) or 38.4 MHz (ICL/TGL+). + */ + if (prop->mclk_freq % 6000000) + syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_38_4; + else + syncprd = SDW_SHIM_SYNC_SYNCPRD_VAL_24; + + if (!*shim_mask) { + /* we first need to program the SyncPRD/CPU registers */ + dev_dbg(sdw->cdns.dev, + "%s: first link up, programming SYNCPRD\n", __func__); + + /* set SyncPRD period */ + sync_reg = intel_readl(shim, SDW_SHIM_SYNC); + sync_reg |= (syncprd << + SDW_REG_SHIFT(SDW_SHIM_SYNC_SYNCPRD)); + + /* Set SyncCPU bit */ + sync_reg |= SDW_SHIM_SYNC_SYNCCPU; + intel_writel(shim, SDW_SHIM_SYNC, sync_reg); + } /* Link power up sequence */ link_control = intel_readl(shim, SDW_SHIM_LCTL); @@ -282,68 +321,180 @@ static int intel_link_power_up(struct sdw_intel *sdw) link_control |= spa_mask; ret = intel_set_bit(shim, SDW_SHIM_LCTL, link_control, cpa_mask); - if (ret < 0) - return ret; + if (ret < 0) { + dev_err(sdw->cdns.dev, "Failed to power up link: %d\n", ret); + goto out; + } + + if (!*shim_mask) { + /* SyncCPU will change once link is active */ + ret = intel_wait_bit(shim, SDW_SHIM_SYNC, + SDW_SHIM_SYNC_SYNCCPU, 0); + if (ret < 0) { + dev_err(sdw->cdns.dev, + "Failed to set SHIM_SYNC: %d\n", ret); + goto out; + } + } + + *shim_mask |= BIT(link_id); sdw->cdns.link_up = true; - return 0; +out: + mutex_unlock(sdw->link_res->shim_lock); + + return ret; } -static int intel_shim_init(struct sdw_intel *sdw) +/* this needs to be called with shim_lock */ +static void intel_shim_glue_to_master_ip(struct sdw_intel *sdw) { void __iomem *shim = sdw->link_res->shim; unsigned int link_id = sdw->instance; - int sync_reg, ret; - u16 ioctl = 0, act = 0; + u16 ioctl; - /* Initialize Shim */ - ioctl |= SDW_SHIM_IOCTL_BKE; + /* Switch to MIP from Glue logic */ + ioctl = intel_readw(shim, SDW_SHIM_IOCTL(link_id)); + + ioctl &= ~(SDW_SHIM_IOCTL_DOE); intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); + usleep_range(10, 15); - ioctl |= SDW_SHIM_IOCTL_WPDD; + ioctl &= ~(SDW_SHIM_IOCTL_DO); intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); + usleep_range(10, 15); - ioctl |= SDW_SHIM_IOCTL_DO; + ioctl |= (SDW_SHIM_IOCTL_MIF); intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); + usleep_range(10, 15); - ioctl |= SDW_SHIM_IOCTL_DOE; + ioctl &= ~(SDW_SHIM_IOCTL_BKE); + ioctl &= ~(SDW_SHIM_IOCTL_COE); intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); + usleep_range(10, 15); - /* Switch to MIP from Glue logic */ - ioctl = intel_readw(shim, SDW_SHIM_IOCTL(link_id)); + /* at this point Master IP has full control of the I/Os */ +} - ioctl &= ~(SDW_SHIM_IOCTL_DOE); +/* this needs to be called with shim_lock */ +static void intel_shim_master_ip_to_glue(struct sdw_intel *sdw) +{ + unsigned int link_id = sdw->instance; + void __iomem *shim = sdw->link_res->shim; + u16 ioctl; + + /* Glue logic */ + ioctl = intel_readw(shim, SDW_SHIM_IOCTL(link_id)); + ioctl |= SDW_SHIM_IOCTL_BKE; + ioctl |= SDW_SHIM_IOCTL_COE; intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); + usleep_range(10, 15); - ioctl &= ~(SDW_SHIM_IOCTL_DO); + ioctl &= ~(SDW_SHIM_IOCTL_MIF); intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); + usleep_range(10, 15); - ioctl |= (SDW_SHIM_IOCTL_MIF); + /* at this point Integration Glue has full control of the I/Os */ +} + +static int intel_shim_init(struct sdw_intel *sdw, bool clock_stop) +{ + void __iomem *shim = sdw->link_res->shim; + unsigned int link_id = sdw->instance; + int ret = 0; + u16 ioctl = 0, act = 0; + + mutex_lock(sdw->link_res->shim_lock); + + /* Initialize Shim */ + ioctl |= SDW_SHIM_IOCTL_BKE; intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); + usleep_range(10, 15); - ioctl &= ~(SDW_SHIM_IOCTL_BKE); - ioctl &= ~(SDW_SHIM_IOCTL_COE); + ioctl |= SDW_SHIM_IOCTL_WPDD; + intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); + usleep_range(10, 15); + ioctl |= SDW_SHIM_IOCTL_DO; + intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); + usleep_range(10, 15); + + ioctl |= SDW_SHIM_IOCTL_DOE; intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); + usleep_range(10, 15); + + intel_shim_glue_to_master_ip(sdw); act |= 0x1 << SDW_REG_SHIFT(SDW_SHIM_CTMCTL_DOAIS); act |= SDW_SHIM_CTMCTL_DACTQE; act |= SDW_SHIM_CTMCTL_DODS; intel_writew(shim, SDW_SHIM_CTMCTL(link_id), act); + usleep_range(10, 15); - /* Now set SyncPRD period */ - sync_reg = intel_readl(shim, SDW_SHIM_SYNC); - sync_reg |= (SDW_SHIM_SYNC_SYNCPRD_VAL << - SDW_REG_SHIFT(SDW_SHIM_SYNC_SYNCPRD)); + mutex_unlock(sdw->link_res->shim_lock); + + return ret; +} + +static void __maybe_unused intel_shim_wake(struct sdw_intel *sdw, bool wake_enable) +{ + void __iomem *shim = sdw->link_res->shim; + unsigned int link_id = sdw->instance; + u16 wake_en, wake_sts; + + mutex_lock(sdw->link_res->shim_lock); + wake_en = intel_readw(shim, SDW_SHIM_WAKEEN); + + if (wake_enable) { + /* Enable the wakeup */ + wake_en |= (SDW_SHIM_WAKEEN_ENABLE << link_id); + intel_writew(shim, SDW_SHIM_WAKEEN, wake_en); + } else { + /* Disable the wake up interrupt */ + wake_en &= ~(SDW_SHIM_WAKEEN_ENABLE << link_id); + intel_writew(shim, SDW_SHIM_WAKEEN, wake_en); + + /* Clear wake status */ + wake_sts = intel_readw(shim, SDW_SHIM_WAKESTS); + wake_sts |= (SDW_SHIM_WAKEEN_ENABLE << link_id); + intel_writew(shim, SDW_SHIM_WAKESTS_STATUS, wake_sts); + } + mutex_unlock(sdw->link_res->shim_lock); +} + +static int __maybe_unused intel_link_power_down(struct sdw_intel *sdw) +{ + int link_control, spa_mask, cpa_mask; + unsigned int link_id = sdw->instance; + void __iomem *shim = sdw->link_res->shim; + u32 *shim_mask = sdw->link_res->shim_mask; + int ret = 0; + + mutex_lock(sdw->link_res->shim_lock); + + intel_shim_master_ip_to_glue(sdw); + + /* Link power down sequence */ + link_control = intel_readl(shim, SDW_SHIM_LCTL); + spa_mask = ~(SDW_SHIM_LCTL_SPA << link_id); + cpa_mask = (SDW_SHIM_LCTL_CPA << link_id); + link_control &= spa_mask; + + ret = intel_clear_bit(shim, SDW_SHIM_LCTL, link_control, cpa_mask); + + if (!(*shim_mask & BIT(link_id))) + dev_err(sdw->cdns.dev, + "%s: Unbalanced power-up/down calls\n", __func__); + + *shim_mask &= ~BIT(link_id); + + mutex_unlock(sdw->link_res->shim_lock); - /* Set SyncCPU bit */ - sync_reg |= SDW_SHIM_SYNC_SYNCCPU; - ret = intel_clear_bit(shim, SDW_SHIM_SYNC, sync_reg, - SDW_SHIM_SYNC_SYNCCPU); if (ret < 0) - dev_err(sdw->cdns.dev, "Failed to set sync period: %d\n", ret); + return ret; - return ret; + sdw->cdns.link_up = false; + return 0; } /* @@ -566,11 +717,15 @@ static int intel_pre_bank_switch(struct sdw_bus *bus) if (!bus->multi_link) return 0; + mutex_lock(sdw->link_res->shim_lock); + /* Read SYNC register */ sync_reg = intel_readl(shim, SDW_SHIM_SYNC); sync_reg |= SDW_SHIM_SYNC_CMDSYNC << sdw->instance; intel_writel(shim, SDW_SHIM_SYNC, sync_reg); + mutex_unlock(sdw->link_res->shim_lock); + return 0; } @@ -585,6 +740,8 @@ static int intel_post_bank_switch(struct sdw_bus *bus) if (!bus->multi_link) return 0; + mutex_lock(sdw->link_res->shim_lock); + /* Read SYNC register */ sync_reg = intel_readl(shim, SDW_SHIM_SYNC); @@ -596,9 +753,10 @@ static int intel_post_bank_switch(struct sdw_bus *bus) * * So, set the SYNCGO bit only if CMDSYNC bit is set for any Master. */ - if (!(sync_reg & SDW_SHIM_SYNC_CMDSYNC_MASK)) - return 0; - + if (!(sync_reg & SDW_SHIM_SYNC_CMDSYNC_MASK)) { + ret = 0; + goto unlock; + } /* * Set SyncGO bit to synchronously trigger a bank switch for * all the masters. A write to SYNCGO bit clears CMDSYNC bit for all @@ -608,6 +766,9 @@ static int intel_post_bank_switch(struct sdw_bus *bus) ret = intel_clear_bit(shim, SDW_SHIM_SYNC, sync_reg, SDW_SHIM_SYNC_SYNCGO); +unlock: + mutex_unlock(sdw->link_res->shim_lock); + if (ret < 0) dev_err(sdw->cdns.dev, "Post bank switch failed: %d\n", ret); @@ -1011,9 +1172,17 @@ static struct sdw_master_ops sdw_intel_ops = { static int intel_init(struct sdw_intel *sdw) { + bool clock_stop; + /* Initialize shim and controller */ intel_link_power_up(sdw); - intel_shim_init(sdw); + + clock_stop = sdw_cdns_is_clock_stop(&sdw->cdns); + + intel_shim_init(sdw, clock_stop); + + if (clock_stop) + return 0; return sdw_cdns_init(&sdw->cdns); } diff --git a/drivers/soundwire/intel.h b/drivers/soundwire/intel.h index 694117370ac3..d6bdd4d63e08 100644 --- a/drivers/soundwire/intel.h +++ b/drivers/soundwire/intel.h @@ -15,6 +15,8 @@ * @irq: Interrupt line * @ops: Shim callback ops * @dev: device implementing hw_params and free callbacks + * @shim_lock: mutex to handle access to shared SHIM registers + * @shim_mask: global pointer to check SHIM register initialization */ struct sdw_intel_link_res { struct platform_device *pdev; @@ -25,6 +27,8 @@ struct sdw_intel_link_res { int irq; const struct sdw_intel_ops *ops; struct device *dev; + struct mutex *shim_lock; /* protect shared registers */ + u32 *shim_mask; }; struct sdw_intel { diff --git a/drivers/soundwire/intel_init.c b/drivers/soundwire/intel_init.c index 3f2e884b4f6d..f50a93130d12 100644 --- a/drivers/soundwire/intel_init.c +++ b/drivers/soundwire/intel_init.c @@ -180,6 +180,7 @@ static struct sdw_intel_ctx ctx->mmio_base = res->mmio_base; ctx->link_mask = res->link_mask; ctx->handle = res->handle; + mutex_init(&ctx->shim_lock); link = ctx->links; link_mask = ctx->link_mask; @@ -201,6 +202,9 @@ static struct sdw_intel_ctx link->ops = res->ops; link->dev = res->dev; + link->shim_lock = &ctx->shim_lock; + link->shim_mask = &ctx->shim_mask; + memset(&pdevinfo, 0, sizeof(pdevinfo)); pdevinfo.parent = res->parent; diff --git a/include/linux/soundwire/sdw_intel.h b/include/linux/soundwire/sdw_intel.h index 979b41b5dcb4..120ffddc03d2 100644 --- a/include/linux/soundwire/sdw_intel.h +++ b/include/linux/soundwire/sdw_intel.h @@ -115,6 +115,7 @@ struct sdw_intel_slave_id { * links * @link_list: list to handle interrupts across all links * @shim_lock: mutex to handle concurrent rmw access to shared SHIM registers. + * @shim_mask: flags to track initialization of SHIM shared registers */ struct sdw_intel_ctx { int count; @@ -126,6 +127,7 @@ struct sdw_intel_ctx { struct sdw_intel_slave_id *ids; struct list_head link_list; struct mutex shim_lock; /* lock for access to shared SHIM registers */ + u32 shim_mask; }; /** -- cgit v1.2.3-59-g8ed1b From 02629e4548277e0abfd334385e96b32e474c6f60 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 16 Jul 2020 23:09:41 +0800 Subject: soundwire: intel: introduce a helper to arm link synchronization Move code from pre_bank_switch to dedicated helper, will be used in follow-up patches as recommended by programming flows. Signed-off-by: Pierre-Louis Bossart Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20200716150947.22119-4-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/intel.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 4792613e8e5a..6a745602c9cc 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -497,6 +497,21 @@ static int __maybe_unused intel_link_power_down(struct sdw_intel *sdw) return 0; } +static void intel_shim_sync_arm(struct sdw_intel *sdw) +{ + void __iomem *shim = sdw->link_res->shim; + u32 sync_reg; + + mutex_lock(sdw->link_res->shim_lock); + + /* update SYNC register */ + sync_reg = intel_readl(shim, SDW_SHIM_SYNC); + sync_reg |= (SDW_SHIM_SYNC_CMDSYNC << sdw->instance); + intel_writel(shim, SDW_SHIM_SYNC, sync_reg); + + mutex_unlock(sdw->link_res->shim_lock); +} + /* * PDI routines */ @@ -710,21 +725,12 @@ static int intel_pre_bank_switch(struct sdw_bus *bus) { struct sdw_cdns *cdns = bus_to_cdns(bus); struct sdw_intel *sdw = cdns_to_intel(cdns); - void __iomem *shim = sdw->link_res->shim; - int sync_reg; /* Write to register only for multi-link */ if (!bus->multi_link) return 0; - mutex_lock(sdw->link_res->shim_lock); - - /* Read SYNC register */ - sync_reg = intel_readl(shim, SDW_SHIM_SYNC); - sync_reg |= SDW_SHIM_SYNC_CMDSYNC << sdw->instance; - intel_writel(shim, SDW_SHIM_SYNC, sync_reg); - - mutex_unlock(sdw->link_res->shim_lock); + intel_shim_sync_arm(sdw); return 0; } -- cgit v1.2.3-59-g8ed1b From 437e3289b5eadb62803f024eea6e85ecb7adf6a2 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 16 Jul 2020 23:09:42 +0800 Subject: soundwire: intel: introduce helper for link synchronization After arming the synchronization, the SYNCGO field controls the hardware-based synchronization between links. Move the programming and wait for clear of SYNCGO to dedicated helper. Signed-off-by: Pierre-Louis Bossart Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20200716150947.22119-5-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/intel.c | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 6a745602c9cc..0a4fc7f65743 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -512,6 +512,31 @@ static void intel_shim_sync_arm(struct sdw_intel *sdw) mutex_unlock(sdw->link_res->shim_lock); } +static int intel_shim_sync_go_unlocked(struct sdw_intel *sdw) +{ + void __iomem *shim = sdw->link_res->shim; + u32 sync_reg; + int ret; + + /* Read SYNC register */ + sync_reg = intel_readl(shim, SDW_SHIM_SYNC); + + /* + * Set SyncGO bit to synchronously trigger a bank switch for + * all the masters. A write to SYNCGO bit clears CMDSYNC bit for all + * the Masters. + */ + sync_reg |= SDW_SHIM_SYNC_SYNCGO; + + ret = intel_clear_bit(shim, SDW_SHIM_SYNC, sync_reg, + SDW_SHIM_SYNC_SYNCGO); + + if (ret < 0) + dev_err(sdw->cdns.dev, "SyncGO clear failed: %d\n", ret); + + return ret; +} + /* * PDI routines */ @@ -763,15 +788,8 @@ static int intel_post_bank_switch(struct sdw_bus *bus) ret = 0; goto unlock; } - /* - * Set SyncGO bit to synchronously trigger a bank switch for - * all the masters. A write to SYNCGO bit clears CMDSYNC bit for all - * the Masters. - */ - sync_reg |= SDW_SHIM_SYNC_SYNCGO; - ret = intel_clear_bit(shim, SDW_SHIM_SYNC, sync_reg, - SDW_SHIM_SYNC_SYNCGO); + ret = intel_shim_sync_go_unlocked(sdw); unlock: mutex_unlock(sdw->link_res->shim_lock); -- cgit v1.2.3-59-g8ed1b From 12b161468d51c732b3d13ddd9ba707774b8096d1 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 16 Jul 2020 23:09:43 +0800 Subject: soundwire: intel_init: add implementation of sdw_intel_enable_irq() This function is required to enable all interrupts across all links. Signed-off-by: Pierre-Louis Bossart Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20200716150947.22119-6-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/intel_init.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/drivers/soundwire/intel_init.c b/drivers/soundwire/intel_init.c index f50a93130d12..d8f0c1472f1f 100644 --- a/drivers/soundwire/intel_init.c +++ b/drivers/soundwire/intel_init.c @@ -142,6 +142,30 @@ sdw_intel_scan_controller(struct sdw_intel_acpi_info *info) return 0; } +#define HDA_DSP_REG_ADSPIC2 (0x10) +#define HDA_DSP_REG_ADSPIS2 (0x14) +#define HDA_DSP_REG_ADSPIC2_SNDW BIT(5) + +/** + * sdw_intel_enable_irq() - enable/disable Intel SoundWire IRQ + * @mmio_base: The mmio base of the control register + * @enable: true if enable + */ +void sdw_intel_enable_irq(void __iomem *mmio_base, bool enable) +{ + u32 val; + + val = readl(mmio_base + HDA_DSP_REG_ADSPIC2); + + if (enable) + val |= HDA_DSP_REG_ADSPIC2_SNDW; + else + val &= ~HDA_DSP_REG_ADSPIC2_SNDW; + + writel(val, mmio_base + HDA_DSP_REG_ADSPIC2); +} +EXPORT_SYMBOL(sdw_intel_enable_irq); + static struct sdw_intel_ctx *sdw_intel_probe_controller(struct sdw_intel_res *res) { -- cgit v1.2.3-59-g8ed1b From 8459cea755e76d04b3f699acdd92676016909ff0 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 16 Jul 2020 23:09:44 +0800 Subject: soundwire: intel_init: use EXPORT_SYMBOL_NS Make sure all symbols in this soundwire-intel-init module are exported with a namespace. The MODULE_IMPORT_NS will be used in Intel/SOF HDaudio modules to be posted in a separate series. Namespaces are only introduced for the Intel parts of the SoundWire code at this time, in future patches we should also add namespaces for Cadence parts and the SoundWire core. Suggested-by: Greg KH Signed-off-by: Pierre-Louis Bossart Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20200716150947.22119-7-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/intel_init.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/soundwire/intel_init.c b/drivers/soundwire/intel_init.c index d8f0c1472f1f..ad3175272e88 100644 --- a/drivers/soundwire/intel_init.c +++ b/drivers/soundwire/intel_init.c @@ -164,7 +164,7 @@ void sdw_intel_enable_irq(void __iomem *mmio_base, bool enable) writel(val, mmio_base + HDA_DSP_REG_ADSPIC2); } -EXPORT_SYMBOL(sdw_intel_enable_irq); +EXPORT_SYMBOL_NS(sdw_intel_enable_irq, SOUNDWIRE_INTEL_INIT); static struct sdw_intel_ctx *sdw_intel_probe_controller(struct sdw_intel_res *res) @@ -353,7 +353,7 @@ int sdw_intel_acpi_scan(acpi_handle *parent_handle, return sdw_intel_scan_controller(info); } -EXPORT_SYMBOL(sdw_intel_acpi_scan); +EXPORT_SYMBOL_NS(sdw_intel_acpi_scan, SOUNDWIRE_INTEL_INIT); /** * sdw_intel_probe() - SoundWire Intel probe routine @@ -370,7 +370,7 @@ struct sdw_intel_ctx { return sdw_intel_probe_controller(res); } -EXPORT_SYMBOL(sdw_intel_probe); +EXPORT_SYMBOL_NS(sdw_intel_probe, SOUNDWIRE_INTEL_INIT); /** * sdw_intel_startup() - SoundWire Intel startup @@ -383,7 +383,7 @@ int sdw_intel_startup(struct sdw_intel_ctx *ctx) { return sdw_intel_startup_controller(ctx); } -EXPORT_SYMBOL(sdw_intel_startup); +EXPORT_SYMBOL_NS(sdw_intel_startup, SOUNDWIRE_INTEL_INIT); /** * sdw_intel_exit() - SoundWire Intel exit * @ctx: SoundWire context allocated in the probe @@ -394,7 +394,7 @@ void sdw_intel_exit(struct sdw_intel_ctx *ctx) { sdw_intel_cleanup(ctx); } -EXPORT_SYMBOL(sdw_intel_exit); +EXPORT_SYMBOL_NS(sdw_intel_exit, SOUNDWIRE_INTEL_INIT); MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("Intel Soundwire Init Library"); -- cgit v1.2.3-59-g8ed1b From 4a98a6b2fa75cf90863b7898457b211ace587e8a Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Thu, 16 Jul 2020 23:09:45 +0800 Subject: soundwire: intel/cadence: merge Soundwire interrupt handlers/threads The existing code uses one pair of interrupt handler/thread per link but at the hardware level the interrupt is shared. This works fine for legacy PCI interrupts, but leads to timeouts in MSI (Message-Signaled Interrupt) mode, likely due to edges being lost. This patch unifies interrupt handling for all links. The dedicated handler is removed since we use a common one for all shared interrupt sources, and the thread function takes care of dealing with interrupt sources. This partition follows the model used for the SOF IPC on HDaudio platforms, where similar timeout issues were noticed and doing all the interrupt handling/clearing in the thread improved reliability/stability. Validation results with 4 links active in parallel show a night-and-day improvement with no timeouts noticed even during stress tests. Latency and quality of service are not affected by the change - mostly because events on a SoundWire link are throttled by the bus frame rate (typically 8..48kHz). Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200716150947.22119-8-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/cadence_master.c | 18 ++++++++++-------- drivers/soundwire/cadence_master.h | 4 ++++ drivers/soundwire/intel.c | 15 --------------- drivers/soundwire/intel.h | 4 ++++ drivers/soundwire/intel_init.c | 19 +++++++++++++++++++ 5 files changed, 37 insertions(+), 23 deletions(-) diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c index 613dbd415b91..24eafe0aa1c3 100644 --- a/drivers/soundwire/cadence_master.c +++ b/drivers/soundwire/cadence_master.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "bus.h" #include "cadence_master.h" @@ -790,7 +791,7 @@ irqreturn_t sdw_cdns_irq(int irq, void *dev_id) CDNS_MCP_INT_SLAVE_MASK, 0); int_status &= ~CDNS_MCP_INT_SLAVE_MASK; - ret = IRQ_WAKE_THREAD; + schedule_work(&cdns->work); } cdns_writel(cdns, CDNS_MCP_INTSTAT, int_status); @@ -799,13 +800,15 @@ irqreturn_t sdw_cdns_irq(int irq, void *dev_id) EXPORT_SYMBOL(sdw_cdns_irq); /** - * sdw_cdns_thread() - Cadence irq thread handler - * @irq: irq number - * @dev_id: irq context + * To update slave status in a work since we will need to handle + * other interrupts eg. CDNS_MCP_INT_RX_WL during the update slave + * process. + * @work: cdns worker thread */ -irqreturn_t sdw_cdns_thread(int irq, void *dev_id) +static void cdns_update_slave_status_work(struct work_struct *work) { - struct sdw_cdns *cdns = dev_id; + struct sdw_cdns *cdns = + container_of(work, struct sdw_cdns, work); u32 slave0, slave1; dev_dbg_ratelimited(cdns->dev, "Slave status change\n"); @@ -822,9 +825,7 @@ irqreturn_t sdw_cdns_thread(int irq, void *dev_id) cdns_updatel(cdns, CDNS_MCP_INTMASK, CDNS_MCP_INT_SLAVE_MASK, CDNS_MCP_INT_SLAVE_MASK); - return IRQ_HANDLED; } -EXPORT_SYMBOL(sdw_cdns_thread); /* * init routines @@ -1427,6 +1428,7 @@ int sdw_cdns_probe(struct sdw_cdns *cdns) init_completion(&cdns->tx_complete); cdns->bus.port_ops = &cdns_port_ops; + INIT_WORK(&cdns->work, cdns_update_slave_status_work); return 0; } EXPORT_SYMBOL(sdw_cdns_probe); diff --git a/drivers/soundwire/cadence_master.h b/drivers/soundwire/cadence_master.h index b410656f8194..7638858397df 100644 --- a/drivers/soundwire/cadence_master.h +++ b/drivers/soundwire/cadence_master.h @@ -129,6 +129,10 @@ struct sdw_cdns { bool link_up; unsigned int msg_count; + + struct work_struct work; + + struct list_head list; }; #define bus_to_cdns(_bus) container_of(_bus, struct sdw_cdns, bus) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 0a4fc7f65743..06c553d94890 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -1258,21 +1258,7 @@ static int intel_master_probe(struct platform_device *pdev) "SoundWire master %d is disabled, will be ignored\n", bus->link_id); - /* Acquire IRQ */ - ret = request_threaded_irq(sdw->link_res->irq, - sdw_cdns_irq, sdw_cdns_thread, - IRQF_SHARED, KBUILD_MODNAME, cdns); - if (ret < 0) { - dev_err(dev, "unable to grab IRQ %d, disabling device\n", - sdw->link_res->irq); - goto err_init; - } - return 0; - -err_init: - sdw_bus_master_delete(bus); - return ret; } int intel_master_startup(struct platform_device *pdev) @@ -1344,7 +1330,6 @@ static int intel_master_remove(struct platform_device *pdev) if (!bus->prop.hw_disabled) { intel_debugfs_exit(sdw); sdw_cdns_enable_interrupt(cdns, false); - free_irq(sdw->link_res->irq, sdw); snd_soc_unregister_component(dev); } sdw_bus_master_delete(bus); diff --git a/drivers/soundwire/intel.h b/drivers/soundwire/intel.h index d6bdd4d63e08..bf127c88eb51 100644 --- a/drivers/soundwire/intel.h +++ b/drivers/soundwire/intel.h @@ -17,6 +17,8 @@ * @dev: device implementing hw_params and free callbacks * @shim_lock: mutex to handle access to shared SHIM registers * @shim_mask: global pointer to check SHIM register initialization + * @cdns: Cadence master descriptor + * @list: used to walk-through all masters exposed by the same controller */ struct sdw_intel_link_res { struct platform_device *pdev; @@ -29,6 +31,8 @@ struct sdw_intel_link_res { struct device *dev; struct mutex *shim_lock; /* protect shared registers */ u32 *shim_mask; + struct sdw_cdns *cdns; + struct list_head list; }; struct sdw_intel { diff --git a/drivers/soundwire/intel_init.c b/drivers/soundwire/intel_init.c index ad3175272e88..a97d3577eb57 100644 --- a/drivers/soundwire/intel_init.c +++ b/drivers/soundwire/intel_init.c @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -166,6 +167,19 @@ void sdw_intel_enable_irq(void __iomem *mmio_base, bool enable) } EXPORT_SYMBOL_NS(sdw_intel_enable_irq, SOUNDWIRE_INTEL_INIT); +irqreturn_t sdw_intel_thread(int irq, void *dev_id) +{ + struct sdw_intel_ctx *ctx = dev_id; + struct sdw_intel_link_res *link; + + list_for_each_entry(link, &ctx->link_list, list) + sdw_cdns_irq(irq, link->cdns); + + sdw_intel_enable_irq(ctx->mmio_base, true); + return IRQ_HANDLED; +} +EXPORT_SYMBOL_NS(sdw_intel_thread, SOUNDWIRE_INTEL_INIT); + static struct sdw_intel_ctx *sdw_intel_probe_controller(struct sdw_intel_res *res) { @@ -209,6 +223,8 @@ static struct sdw_intel_ctx link = ctx->links; link_mask = ctx->link_mask; + INIT_LIST_HEAD(&ctx->link_list); + /* Create SDW Master devices */ for (i = 0; i < count; i++, link++) { if (!(link_mask & BIT(i))) { @@ -246,6 +262,9 @@ static struct sdw_intel_ctx goto err; } link->pdev = pdev; + link->cdns = platform_get_drvdata(pdev); + + list_add_tail(&link->list, &ctx->link_list); } return ctx; -- cgit v1.2.3-59-g8ed1b From ab2c913297a1a9a62d6610023ba335b2f6bc8a04 Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Thu, 16 Jul 2020 23:09:46 +0800 Subject: soundwire: intel: add wake interrupt support When system is suspended in clock stop mode on intel platforms, both master and slave are in clock stop mode and soundwire bus is taken over by a glue hardware. The bus message for jack event is processed by this glue hardware, which will trigger an interrupt to resume audio pci device. Then audio pci driver will resume soundwire master and slave, transfer bus ownership to master, finally slave will report jack event to master and codec driver is triggered to check jack status. if a slave has been attached to a bus, the slave->dev_num_sticky should be non-zero, so we can check this value to skip the ghost devices defined in ACPI table but not populated in hardware. Signed-off-by: Rander Wang Signed-off-by: Pierre-Louis Bossart Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20200716150947.22119-9-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/intel.c | 40 +++++++++++++++++++++++++++++++++++++++- drivers/soundwire/intel.h | 1 + drivers/soundwire/intel_init.c | 22 ++++++++++++++++++++++ 3 files changed, 62 insertions(+), 1 deletion(-) diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 06c553d94890..23b66dcf9966 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -436,7 +437,7 @@ static int intel_shim_init(struct sdw_intel *sdw, bool clock_stop) return ret; } -static void __maybe_unused intel_shim_wake(struct sdw_intel *sdw, bool wake_enable) +static void intel_shim_wake(struct sdw_intel *sdw, bool wake_enable) { void __iomem *shim = sdw->link_res->shim; unsigned int link_id = sdw->instance; @@ -1337,6 +1338,43 @@ static int intel_master_remove(struct platform_device *pdev) return 0; } +int intel_master_process_wakeen_event(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct sdw_intel *sdw; + struct sdw_bus *bus; + void __iomem *shim; + u16 wake_sts; + + sdw = platform_get_drvdata(pdev); + bus = &sdw->cdns.bus; + + if (bus->prop.hw_disabled) { + dev_dbg(dev, "SoundWire master %d is disabled, ignoring\n", bus->link_id); + return 0; + } + + shim = sdw->link_res->shim; + wake_sts = intel_readw(shim, SDW_SHIM_WAKESTS); + + if (!(wake_sts & BIT(sdw->instance))) + return 0; + + /* disable WAKEEN interrupt ASAP to prevent interrupt flood */ + intel_shim_wake(sdw, false); + + /* + * resume the Master, which will generate a bus reset and result in + * Slaves re-attaching and be re-enumerated. The SoundWire physical + * device which generated the wake will trigger an interrupt, which + * will in turn cause the corresponding Linux Slave device to be + * resumed and the Slave codec driver to check the status. + */ + pm_request_resume(dev); + + return 0; +} + static struct platform_driver sdw_intel_drv = { .probe = intel_master_probe, .remove = intel_master_remove, diff --git a/drivers/soundwire/intel.h b/drivers/soundwire/intel.h index bf127c88eb51..4ea3d262d249 100644 --- a/drivers/soundwire/intel.h +++ b/drivers/soundwire/intel.h @@ -47,5 +47,6 @@ struct sdw_intel { #define SDW_INTEL_QUIRK_MASK_BUS_DISABLE BIT(1) int intel_master_startup(struct platform_device *pdev); +int intel_master_process_wakeen_event(struct platform_device *pdev); #endif /* __SDW_INTEL_LOCAL_H */ diff --git a/drivers/soundwire/intel_init.c b/drivers/soundwire/intel_init.c index a97d3577eb57..8d3992f9fcdd 100644 --- a/drivers/soundwire/intel_init.c +++ b/drivers/soundwire/intel_init.c @@ -415,5 +415,27 @@ void sdw_intel_exit(struct sdw_intel_ctx *ctx) } EXPORT_SYMBOL_NS(sdw_intel_exit, SOUNDWIRE_INTEL_INIT); +void sdw_intel_process_wakeen_event(struct sdw_intel_ctx *ctx) +{ + struct sdw_intel_link_res *link; + u32 link_mask; + int i; + + if (!ctx->links) + return; + + link = ctx->links; + link_mask = ctx->link_mask; + + /* Startup SDW Master devices */ + for (i = 0; i < ctx->count; i++, link++) { + if (!(link_mask & BIT(i))) + continue; + + intel_master_process_wakeen_event(link->pdev); + } +} +EXPORT_SYMBOL_NS(sdw_intel_process_wakeen_event, SOUNDWIRE_INTEL_INIT); + MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("Intel Soundwire Init Library"); -- cgit v1.2.3-59-g8ed1b From a81844034e5b382df6cc06bc2943ae24be797afd Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Thu, 16 Jul 2020 23:09:47 +0800 Subject: Soundwire: intel_init: save Slave(s) _ADR info in sdw_intel_ctx Save ACPI information in context so that we can match machine driver with sdw _ADR matching tables. Suggested-by: Guennadi Liakhovetski Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200716150947.22119-10-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/intel_init.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/drivers/soundwire/intel_init.c b/drivers/soundwire/intel_init.c index 8d3992f9fcdd..047252a91c9e 100644 --- a/drivers/soundwire/intel_init.c +++ b/drivers/soundwire/intel_init.c @@ -188,7 +188,11 @@ static struct sdw_intel_ctx struct sdw_intel_link_res *link; struct sdw_intel_ctx *ctx; struct acpi_device *adev; + struct sdw_slave *slave; + struct list_head *node; + struct sdw_bus *bus; u32 link_mask; + int num_slaves = 0; int count; int i; @@ -265,6 +269,26 @@ static struct sdw_intel_ctx link->cdns = platform_get_drvdata(pdev); list_add_tail(&link->list, &ctx->link_list); + bus = &link->cdns->bus; + /* Calculate number of slaves */ + list_for_each(node, &bus->slaves) + num_slaves++; + } + + ctx->ids = devm_kcalloc(&adev->dev, num_slaves, + sizeof(*ctx->ids), GFP_KERNEL); + if (!ctx->ids) + goto err; + + ctx->num_slaves = num_slaves; + i = 0; + list_for_each_entry(link, &ctx->link_list, list) { + bus = &link->cdns->bus; + list_for_each_entry(slave, &bus->slaves, node) { + ctx->ids[i].id = slave->id; + ctx->ids[i].link_id = bus->link_id; + i++; + } } return ctx; -- cgit v1.2.3-59-g8ed1b From e9f84ec63c6f0f2fecd3c5bdec0178bb927c0c77 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Mon, 20 Jul 2020 08:27:18 -0500 Subject: phy: samsung-ufs: Fix IS_ERR argument Fix IS_ERR argument in samsung_ufs_phy_symbol_clk_init(). The proper argument to be passed to IS_ERR() is phy->rx1_symbol_clk. This bug was detected with the help of Coccinelle. Fixes: bca21e930451 ("phy: samsung-ufs: add UFS PHY driver for samsung SoC") Signed-off-by: Gustavo A. R. Silva Reviewed-by: Alim Akhtar Link: https://lore.kernel.org/r/20200720132718.GA13413@embeddedor Signed-off-by: Vinod Koul --- drivers/phy/samsung/phy-samsung-ufs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/samsung/phy-samsung-ufs.c b/drivers/phy/samsung/phy-samsung-ufs.c index 43ef77d1d96c..9832599a0283 100644 --- a/drivers/phy/samsung/phy-samsung-ufs.c +++ b/drivers/phy/samsung/phy-samsung-ufs.c @@ -147,7 +147,7 @@ static int samsung_ufs_phy_symbol_clk_init(struct samsung_ufs_phy *phy) } phy->rx1_symbol_clk = devm_clk_get(phy->dev, "rx1_symbol_clk"); - if (IS_ERR(phy->rx0_symbol_clk)) { + if (IS_ERR(phy->rx1_symbol_clk)) { dev_err(phy->dev, "failed to get rx1_symbol_clk clock\n"); return PTR_ERR(phy->rx1_symbol_clk); } -- cgit v1.2.3-59-g8ed1b From 9c1a58b77e8d34b36f7d98bce6cc7d11478c001e Mon Sep 17 00:00:00 2001 From: Sai Prakash Ranjan Date: Thu, 16 Jul 2020 11:57:30 -0600 Subject: coresight: replicator: Use CS_AMBA_ID macro for id table Use CS_AMBA_ID macro for dynamic replicator AMBA id table instead of open coding. Signed-off-by: Sai Prakash Ranjan Signed-off-by: Mathieu Poirier Link: https://lore.kernel.org/r/20200716175746.3338735-2-mathieu.poirier@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-replicator.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-replicator.c b/drivers/hwtracing/coresight/coresight-replicator.c index e7dc1c31d20d..c619b456f55a 100644 --- a/drivers/hwtracing/coresight/coresight-replicator.c +++ b/drivers/hwtracing/coresight/coresight-replicator.c @@ -348,16 +348,9 @@ static int dynamic_replicator_probe(struct amba_device *adev, } static const struct amba_id dynamic_replicator_ids[] = { - { - .id = 0x000bb909, - .mask = 0x000fffff, - }, - { - /* Coresight SoC-600 */ - .id = 0x000bb9ec, - .mask = 0x000fffff, - }, - { 0, 0 }, + CS_AMBA_ID(0x000bb909), + CS_AMBA_ID(0x000bb9ec), /* Coresight SoC-600 */ + {}, }; static struct amba_driver dynamic_replicator_driver = { -- cgit v1.2.3-59-g8ed1b From 9cfb556357d56895ae50f0f3c691730d640530d2 Mon Sep 17 00:00:00 2001 From: Sai Prakash Ranjan Date: Thu, 16 Jul 2020 11:57:31 -0600 Subject: coresight: catu: Use CS_AMBA_ID macro for id table Use CS_AMBA_ID macro for coresight catu AMBA id table instead of open coding. Signed-off-by: Sai Prakash Ranjan Signed-off-by: Mathieu Poirier Link: https://lore.kernel.org/r/20200716175746.3338735-3-mathieu.poirier@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-catu.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-catu.c b/drivers/hwtracing/coresight/coresight-catu.c index 16ebf38a9f66..1801804a7762 100644 --- a/drivers/hwtracing/coresight/coresight-catu.c +++ b/drivers/hwtracing/coresight/coresight-catu.c @@ -568,10 +568,7 @@ out: } static struct amba_id catu_ids[] = { - { - .id = 0x000bb9ee, - .mask = 0x000fffff, - }, + CS_AMBA_ID(0x000bb9ee), {}, }; -- cgit v1.2.3-59-g8ed1b From 02510a5aa78df45ff9a22def37e5c18677d4d659 Mon Sep 17 00:00:00 2001 From: Tingwei Zhang Date: Thu, 16 Jul 2020 11:57:32 -0600 Subject: coresight: etm4x: Add support to skip trace unit power up On some Qualcomm Technologies Inc. SoCs like SC7180, there exists a hardware errata where the APSS (Application Processor SubSystem)/CPU watchdog counter is stopped when the trace unit power up ETM register is set (TRCPDCR.PU = 1). Since the ETMs share the same power domain as that of respective CPU cores, they are powered on when the CPU core is powered on. So we can skip powering up of trace unit after checking for this errata via new property called "qcom,skip-power-up". Signed-off-by: Tingwei Zhang Co-developed-by: Sai Prakash Ranjan Signed-off-by: Sai Prakash Ranjan Signed-off-by: Mathieu Poirier Link: https://lore.kernel.org/r/20200716175746.3338735-4-mathieu.poirier@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etm4x.c | 27 +++++++++++++++++---------- drivers/hwtracing/coresight/coresight-etm4x.h | 3 +++ 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 0c35cd5e0d1d..2290f41f0074 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -196,12 +196,14 @@ static int etm4_enable_hw(struct etmv4_drvdata *drvdata) writel_relaxed(config->vmid_mask0, drvdata->base + TRCVMIDCCTLR0); writel_relaxed(config->vmid_mask1, drvdata->base + TRCVMIDCCTLR1); - /* - * Request to keep the trace unit powered and also - * emulation of powerdown - */ - writel_relaxed(readl_relaxed(drvdata->base + TRCPDCR) | TRCPDCR_PU, - drvdata->base + TRCPDCR); + if (!drvdata->skip_power_up) { + /* + * Request to keep the trace unit powered and also + * emulation of powerdown + */ + writel_relaxed(readl_relaxed(drvdata->base + TRCPDCR) | + TRCPDCR_PU, drvdata->base + TRCPDCR); + } /* Enable the trace unit */ writel_relaxed(1, drvdata->base + TRCPRGCTLR); @@ -476,10 +478,12 @@ static void etm4_disable_hw(void *info) CS_UNLOCK(drvdata->base); - /* power can be removed from the trace unit now */ - control = readl_relaxed(drvdata->base + TRCPDCR); - control &= ~TRCPDCR_PU; - writel_relaxed(control, drvdata->base + TRCPDCR); + if (!drvdata->skip_power_up) { + /* power can be removed from the trace unit now */ + control = readl_relaxed(drvdata->base + TRCPDCR); + control &= ~TRCPDCR_PU; + writel_relaxed(control, drvdata->base + TRCPDCR); + } control = readl_relaxed(drvdata->base + TRCPRGCTLR); @@ -1468,6 +1472,9 @@ static int etm4_probe(struct amba_device *adev, const struct amba_id *id) return -ENOMEM; } + if (fwnode_property_present(dev_fwnode(dev), "qcom,skip-power-up")) + drvdata->skip_power_up = true; + /* Validity for the resource is already checked by the AMBA core */ base = devm_ioremap_resource(dev, res); if (IS_ERR(base)) diff --git a/drivers/hwtracing/coresight/coresight-etm4x.h b/drivers/hwtracing/coresight/coresight-etm4x.h index 4a695bf90582..72c9a55e67df 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.h +++ b/drivers/hwtracing/coresight/coresight-etm4x.h @@ -407,6 +407,8 @@ struct etmv4_save_state { * @config: structure holding configuration parameters. * @save_state: State to be preserved across power loss * @state_needs_restore: True when there is context to restore after PM exit + * @skip_power_up: Indicates if an implementation can skip powering up + * the trace unit. */ struct etmv4_drvdata { void __iomem *base; @@ -454,6 +456,7 @@ struct etmv4_drvdata { struct etmv4_config config; struct etmv4_save_state *save_state; bool state_needs_restore; + bool skip_power_up; }; /* Address comparator access types */ -- cgit v1.2.3-59-g8ed1b From 7f1a1c2c57dbda7278ef06700efcac63433b9893 Mon Sep 17 00:00:00 2001 From: Tingwei Zhang Date: Thu, 16 Jul 2020 11:57:33 -0600 Subject: dt-bindings: arm: coresight: Add support to skip trace unit power up Add "qcom,skip-power-up" property to identify systems which can skip powering up of trace unit since they share the same power domain as their CPU core. This is required to identify such systems with hardware errata which stops the CPU watchdog counter when the power up bit is set (TRCPDCR.PU). Signed-off-by: Tingwei Zhang Co-developed-by: Sai Prakash Ranjan Signed-off-by: Sai Prakash Ranjan Reviewed-by: Rob Herring Signed-off-by: Mathieu Poirier Link: https://lore.kernel.org/r/20200716175746.3338735-5-mathieu.poirier@linaro.org Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/arm/coresight.txt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Documentation/devicetree/bindings/arm/coresight.txt b/Documentation/devicetree/bindings/arm/coresight.txt index 846f6daae71b..e4b2eda0b53b 100644 --- a/Documentation/devicetree/bindings/arm/coresight.txt +++ b/Documentation/devicetree/bindings/arm/coresight.txt @@ -108,6 +108,13 @@ its hardware characteristcs. * arm,cp14: must be present if the system accesses ETM/PTM management registers via co-processor 14. + * qcom,skip-power-up: boolean. Indicates that an implementation can + skip powering up the trace unit. TRCPDCR.PU does not have to be set + on Qualcomm Technologies Inc. systems since ETMs are in the same power + domain as their CPU cores. This property is required to identify such + systems with hardware errata where the CPU watchdog counter is stopped + when TRCPDCR.PU is set. + * Optional property for TMC: * arm,buffer-size: size of contiguous buffer space for TMC ETR -- cgit v1.2.3-59-g8ed1b From 8f3ce74c20f21e09644d2a584238f1453a00eb8d Mon Sep 17 00:00:00 2001 From: Sai Prakash Ranjan Date: Thu, 16 Jul 2020 11:57:34 -0600 Subject: coresight: replicator: Reset replicator if context is lost On some QCOM SoCs, replicators in Always-On domain loses its context as soon as the clock is disabled. Currently as a part of pm_runtime workqueue, clock is disabled after the replicator is initialized by amba_pm_runtime_suspend assuming that context is not lost which is not true for replicators with such limitations. So add a new property "qcom,replicator-loses-context" to identify such replicators and reset them. Suggested-by: Mike Leach Reviewed-by: Mike Leach Signed-off-by: Sai Prakash Ranjan Signed-off-by: Mathieu Poirier Link: https://lore.kernel.org/r/20200716175746.3338735-6-mathieu.poirier@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-replicator.c | 55 +++++++++++++++------- 1 file changed, 38 insertions(+), 17 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-replicator.c b/drivers/hwtracing/coresight/coresight-replicator.c index c619b456f55a..78acf29c49ca 100644 --- a/drivers/hwtracing/coresight/coresight-replicator.c +++ b/drivers/hwtracing/coresight/coresight-replicator.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -32,12 +33,14 @@ DEFINE_CORESIGHT_DEVLIST(replicator_devs, "replicator"); * @atclk: optional clock for the core parts of the replicator. * @csdev: component vitals needed by the framework * @spinlock: serialize enable/disable operations. + * @check_idfilter_val: check if the context is lost upon clock removal. */ struct replicator_drvdata { void __iomem *base; struct clk *atclk; struct coresight_device *csdev; spinlock_t spinlock; + bool check_idfilter_val; }; static void dynamic_replicator_reset(struct replicator_drvdata *drvdata) @@ -66,29 +69,43 @@ static int dynamic_replicator_enable(struct replicator_drvdata *drvdata, int inport, int outport) { int rc = 0; - u32 reg; - - switch (outport) { - case 0: - reg = REPLICATOR_IDFILTER0; - break; - case 1: - reg = REPLICATOR_IDFILTER1; - break; - default: - WARN_ON(1); - return -EINVAL; - } + u32 id0val, id1val; CS_UNLOCK(drvdata->base); - if ((readl_relaxed(drvdata->base + REPLICATOR_IDFILTER0) == 0xff) && - (readl_relaxed(drvdata->base + REPLICATOR_IDFILTER1) == 0xff)) + id0val = readl_relaxed(drvdata->base + REPLICATOR_IDFILTER0); + id1val = readl_relaxed(drvdata->base + REPLICATOR_IDFILTER1); + + /* + * Some replicator designs lose context when AMBA clocks are removed, + * so have a check for this. + */ + if (drvdata->check_idfilter_val && id0val == 0x0 && id1val == 0x0) + id0val = id1val = 0xff; + + if (id0val == 0xff && id1val == 0xff) rc = coresight_claim_device_unlocked(drvdata->base); + if (!rc) { + switch (outport) { + case 0: + id0val = 0x0; + break; + case 1: + id1val = 0x0; + break; + default: + WARN_ON(1); + rc = -EINVAL; + } + } + /* Ensure that the outport is enabled. */ - if (!rc) - writel_relaxed(0x00, drvdata->base + reg); + if (!rc) { + writel_relaxed(id0val, drvdata->base + REPLICATOR_IDFILTER0); + writel_relaxed(id1val, drvdata->base + REPLICATOR_IDFILTER1); + } + CS_LOCK(drvdata->base); return rc; @@ -239,6 +256,10 @@ static int replicator_probe(struct device *dev, struct resource *res) desc.groups = replicator_groups; } + if (fwnode_property_present(dev_fwnode(dev), + "qcom,replicator-loses-context")) + drvdata->check_idfilter_val = true; + dev_set_drvdata(dev, drvdata); pdata = coresight_get_platform_data(dev); -- cgit v1.2.3-59-g8ed1b From 784bb9ad490d26bb1ae059a3197b3b1c55ff4e20 Mon Sep 17 00:00:00 2001 From: Sai Prakash Ranjan Date: Thu, 16 Jul 2020 11:57:35 -0600 Subject: dt-bindings: arm: coresight: Add optional property to replicators Add an optional boolean property "qcom,replicator-loses-context" to identify replicators which loses context when AMBA clocks are removed in certain configurable replicator designs. Signed-off-by: Sai Prakash Ranjan Reviewed-by: Mike Leach Acked-by: Rob Herring Signed-off-by: Mathieu Poirier Link: https://lore.kernel.org/r/20200716175746.3338735-7-mathieu.poirier@linaro.org Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/arm/coresight.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/devicetree/bindings/arm/coresight.txt b/Documentation/devicetree/bindings/arm/coresight.txt index e4b2eda0b53b..d711676b4a51 100644 --- a/Documentation/devicetree/bindings/arm/coresight.txt +++ b/Documentation/devicetree/bindings/arm/coresight.txt @@ -128,6 +128,12 @@ its hardware characteristcs. * interrupts : Exactly one SPI may be listed for reporting the address error +* Optional property for configurable replicators: + + * qcom,replicator-loses-context: boolean. Indicates that the replicator + will lose register context when AMBA clock is removed which is observed + in some replicator designs. + Graph bindings for Coresight ------------------------------- -- cgit v1.2.3-59-g8ed1b From 870fdd851ca50ffa552c41fd127afafceb303eca Mon Sep 17 00:00:00 2001 From: Xu Wang Date: Thu, 16 Jul 2020 11:57:36 -0600 Subject: coresight: Use devm_kcalloc() in coresight_alloc_conns() A multiplication for the size determination of a memory allocation indicated that an array data structure should be processed. Thus use the corresponding function "devm_kcalloc". Signed-off-by: Xu Wang Signed-off-by: Mathieu Poirier Link: https://lore.kernel.org/r/20200716175746.3338735-8-mathieu.poirier@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-platform.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-platform.c b/drivers/hwtracing/coresight/coresight-platform.c index e4912abda3aa..bfd44231d7ad 100644 --- a/drivers/hwtracing/coresight/coresight-platform.c +++ b/drivers/hwtracing/coresight/coresight-platform.c @@ -27,9 +27,8 @@ static int coresight_alloc_conns(struct device *dev, struct coresight_platform_data *pdata) { if (pdata->nr_outport) { - pdata->conns = devm_kzalloc(dev, pdata->nr_outport * - sizeof(*pdata->conns), - GFP_KERNEL); + pdata->conns = devm_kcalloc(dev, pdata->nr_outport, + sizeof(*pdata->conns), GFP_KERNEL); if (!pdata->conns) return -ENOMEM; } -- cgit v1.2.3-59-g8ed1b From e82d785eba4a2b047a71d7d6c5ee671237bfe716 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 16 Jul 2020 11:57:37 -0600 Subject: coresight: Drop double check for ACPI companion device acpi_dev_get_resources() does perform the NULL pointer check against ACPI companion device which is given as function parameter. Thus, there is no need to duplicate this check in the caller. Signed-off-by: Andy Shevchenko Signed-off-by: Mathieu Poirier Link: https://lore.kernel.org/r/20200716175746.3338735-9-mathieu.poirier@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-stm.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-stm.c b/drivers/hwtracing/coresight/coresight-stm.c index b908ca104645..673d2f56ed1e 100644 --- a/drivers/hwtracing/coresight/coresight-stm.c +++ b/drivers/hwtracing/coresight/coresight-stm.c @@ -727,8 +727,6 @@ static int acpi_stm_get_stimulus_area(struct device *dev, struct resource *res) struct acpi_device *adev = ACPI_COMPANION(dev); - if (!adev) - return -ENODEV; rc = acpi_dev_get_resources(adev, &res_list, NULL, NULL); if (rc < 0) return rc; -- cgit v1.2.3-59-g8ed1b From cb8bba907a4ff4ba42f1d245cb506d55829674b8 Mon Sep 17 00:00:00 2001 From: Mike Leach Date: Thu, 16 Jul 2020 11:57:38 -0600 Subject: coresight: etmv4: Fix resource selector constant ETMv4 max resource selector constant incorrectly set to 16. Updated to the correct 32 value, and adjustments made to limited code using it. Signed-off-by: Mike Leach Fixes: 2e1cdfe184b52 ("coresight-etm4x: Adding CoreSight ETM4x driver") Signed-off-by: Mathieu Poirier Link: https://lore.kernel.org/r/20200716175746.3338735-10-mathieu.poirier@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etm4x.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm4x.h b/drivers/hwtracing/coresight/coresight-etm4x.h index 72c9a55e67df..7da022e87218 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.h +++ b/drivers/hwtracing/coresight/coresight-etm4x.h @@ -133,7 +133,7 @@ #define ETMv4_MAX_CTXID_CMP 8 #define ETM_MAX_VMID_CMP 8 #define ETM_MAX_PE_CMP 8 -#define ETM_MAX_RES_SEL 16 +#define ETM_MAX_RES_SEL 32 #define ETM_MAX_SS_CMP 8 #define ETM_ARCH_V4 0x40 @@ -325,7 +325,7 @@ struct etmv4_save_state { u32 trccntctlr[ETMv4_MAX_CNTR]; u32 trccntvr[ETMv4_MAX_CNTR]; - u32 trcrsctlr[ETM_MAX_RES_SEL * 2]; + u32 trcrsctlr[ETM_MAX_RES_SEL]; u32 trcssccr[ETM_MAX_SS_CMP]; u32 trcsscsr[ETM_MAX_SS_CMP]; -- cgit v1.2.3-59-g8ed1b From 8fa43700f69703f995ea715b76be6fabdd2f05de Mon Sep 17 00:00:00 2001 From: Mike Leach Date: Thu, 16 Jul 2020 11:57:39 -0600 Subject: coresight: etmv4: Counter values not saved on disable The counter value registers change during operation, however this change is not reflected in the values seen by the user in sysfs. This fixes the issue by reading back the values on disable. Signed-off-by: Mike Leach Fixes: 2e1cdfe184b52 ("coresight-etm4x: Adding CoreSight ETM4x driver") Signed-off-by: Mathieu Poirier Link: https://lore.kernel.org/r/20200716175746.3338735-11-mathieu.poirier@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etm4x.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 2290f41f0074..2d732af8b3e7 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -511,6 +511,12 @@ static void etm4_disable_hw(void *info) readl_relaxed(drvdata->base + TRCSSCSRn(i)); } + /* read back the current counter values */ + for (i = 0; i < drvdata->nr_cntr; i++) { + config->cntr_val[i] = + readl_relaxed(drvdata->base + TRCCNTVRn(i)); + } + coresight_disclaim_device_unlocked(drvdata->base); CS_LOCK(drvdata->base); -- cgit v1.2.3-59-g8ed1b From d061cd734f1a59798574b50188b14b49e95a54d3 Mon Sep 17 00:00:00 2001 From: Mike Leach Date: Thu, 16 Jul 2020 11:57:40 -0600 Subject: coresight: Fix comment in main header file Comment for an elemnt in the coresight_device structure appears to have been corrupted and makes no sense. Fix this before making further changes. Signed-off-by: Mike Leach Reviewed-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier Link: https://lore.kernel.org/r/20200716175746.3338735-12-mathieu.poirier@linaro.org Signed-off-by: Greg Kroah-Hartman --- include/linux/coresight.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/linux/coresight.h b/include/linux/coresight.h index e3e9f0e3a878..84dc695e87d4 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -179,7 +179,8 @@ struct coresight_sysfs_link { * @enable: 'true' if component is currently part of an active path. * @activated: 'true' only if a _sink_ has been activated. A sink can be * activated but not yet enabled. Enabling for a _sink_ - * appens when a source has been selected for that it. + * happens when a source has been selected and a path is enabled + * from source to that sink. * @ea: Device attribute for sink representation under PMU directory. * @ect_dev: Associated cross trigger device. Not part of the trace data * path or connections. -- cgit v1.2.3-59-g8ed1b From 865d30792591eafeabf4b4d4728df908548d5362 Mon Sep 17 00:00:00 2001 From: Sai Prakash Ranjan Date: Thu, 16 Jul 2020 11:57:41 -0600 Subject: coresight: tmc: Add shutdown callback for TMC ETR Implement a shutdown callback to ensure ETR hardware is properly shutdown in reboot/shutdown path. This is required for ETR which has SMMU address translation enabled like on SC7180 SoC and few others. If the hardware is still accessing memory after SMMU translation is disabled as part of SMMU shutdown callback in system reboot or shutdown path, then IOVAs(I/O virtual address) which it was using will go on the bus as the physical addresses which might result in unknown crashes (NoC/interconnect errors). So we make sure from this shutdown callback that the ETR is shutdown before SMMU translation is disabled and device_link in SMMU driver will take care of ordering of shutdown callbacks such that SMMU shutdown callback is not called before any of its consumer shutdown callbacks. Signed-off-by: Sai Prakash Ranjan Signed-off-by: Mathieu Poirier Link: https://lore.kernel.org/r/20200716175746.3338735-13-mathieu.poirier@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-tmc-etr.c | 2 +- drivers/hwtracing/coresight/coresight-tmc.c | 23 +++++++++++++++++++++++ drivers/hwtracing/coresight/coresight-tmc.h | 1 + 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index 625882bc8b08..b29c2db94d96 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -1110,7 +1110,7 @@ static void __tmc_etr_disable_hw(struct tmc_drvdata *drvdata) } -static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata) +void tmc_etr_disable_hw(struct tmc_drvdata *drvdata) { __tmc_etr_disable_hw(drvdata); /* Disable CATU device if this ETR is connected to one */ diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index 39fba1d16e6e..b13ce0daa572 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -538,6 +538,28 @@ out: return ret; } +static void tmc_shutdown(struct amba_device *adev) +{ + unsigned long flags; + struct tmc_drvdata *drvdata = amba_get_drvdata(adev); + + spin_lock_irqsave(&drvdata->spinlock, flags); + + if (drvdata->mode == CS_MODE_DISABLED) + goto out; + + if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) + tmc_etr_disable_hw(drvdata); + + /* + * We do not care about coresight unregister here unlike remove + * callback which is required for making coresight modular since + * the system is going down after this. + */ +out: + spin_unlock_irqrestore(&drvdata->spinlock, flags); +} + static const struct amba_id tmc_ids[] = { CS_AMBA_ID(0x000bb961), /* Coresight SoC 600 TMC-ETR/ETS */ @@ -556,6 +578,7 @@ static struct amba_driver tmc_driver = { .suppress_bind_attrs = true, }, .probe = tmc_probe, + .shutdown = tmc_shutdown, .id_table = tmc_ids, }; builtin_amba_driver(tmc_driver); diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h index 71de978575f3..6e8d2dc33d17 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.h +++ b/drivers/hwtracing/coresight/coresight-tmc.h @@ -268,6 +268,7 @@ ssize_t tmc_etb_get_sysfs_trace(struct tmc_drvdata *drvdata, /* ETR functions */ int tmc_read_prepare_etr(struct tmc_drvdata *drvdata); int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata); +void tmc_etr_disable_hw(struct tmc_drvdata *drvdata); extern const struct coresight_ops tmc_etr_cs_ops; ssize_t tmc_etr_get_sysfs_trace(struct tmc_drvdata *drvdata, loff_t pos, size_t len, char **bufpp); -- cgit v1.2.3-59-g8ed1b From d021f5c5ff679432c5e9faee0fd7350db2efb97c Mon Sep 17 00:00:00 2001 From: Sai Prakash Ranjan Date: Thu, 16 Jul 2020 11:57:42 -0600 Subject: coresight: tmc: Fix TMC mode read in tmc_read_unprepare_etb() Reading TMC mode register without proper coresight power management can lead to exceptions like the one in the call trace below in tmc_read_unprepare_etb() when the trace data is read after the sink is disabled. So fix this by having a check for coresight sysfs mode before reading TMC mode management register in tmc_read_unprepare_etb() similar to tmc_read_prepare_etb(). SError Interrupt on CPU6, code 0xbe000411 -- SError pstate: 80400089 (Nzcv daIf +PAN -UAO) pc : tmc_read_unprepare_etb+0x74/0x108 lr : tmc_read_unprepare_etb+0x54/0x108 sp : ffffff80d9507c30 x29: ffffff80d9507c30 x28: ffffff80b3569a0c x27: 0000000000000000 x26: 00000000000a0001 x25: ffffff80cbae9550 x24: 0000000000000010 x23: ffffffd07296b0f0 x22: ffffffd0109ee028 x21: 0000000000000000 x20: ffffff80d19e70e0 x19: ffffff80d19e7080 x18: 0000000000000000 x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000 x14: 0000000000000000 x13: 0000000000000000 x12: 0000000000000000 x11: 0000000000000000 x10: dfffffd000000001 x9 : 0000000000000000 x8 : 0000000000000002 x7 : ffffffd071d0fe78 x6 : 0000000000000000 x5 : 0000000000000080 x4 : 0000000000000001 x3 : ffffffd071d0fe98 x2 : 0000000000000000 x1 : 0000000000000004 x0 : 0000000000000001 Kernel panic - not syncing: Asynchronous SError Interrupt Fixes: 4525412a5046 ("coresight: tmc: making prepare/unprepare functions generic") Reported-by: Mike Leach Signed-off-by: Sai Prakash Ranjan Tested-by: Mike Leach Signed-off-by: Mathieu Poirier Link: https://lore.kernel.org/r/20200716175746.3338735-14-mathieu.poirier@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-tmc-etf.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c index 36cce2bfb744..6375504ba8b0 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etf.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c @@ -639,15 +639,14 @@ int tmc_read_unprepare_etb(struct tmc_drvdata *drvdata) spin_lock_irqsave(&drvdata->spinlock, flags); - /* There is no point in reading a TMC in HW FIFO mode */ - mode = readl_relaxed(drvdata->base + TMC_MODE); - if (mode != TMC_MODE_CIRCULAR_BUFFER) { - spin_unlock_irqrestore(&drvdata->spinlock, flags); - return -EINVAL; - } - /* Re-enable the TMC if need be */ if (drvdata->mode == CS_MODE_SYSFS) { + /* There is no point in reading a TMC in HW FIFO mode */ + mode = readl_relaxed(drvdata->base + TMC_MODE); + if (mode != TMC_MODE_CIRCULAR_BUFFER) { + spin_unlock_irqrestore(&drvdata->spinlock, flags); + return -EINVAL; + } /* * The trace run will continue with the same allocated trace * buffer. As such zero-out the buffer so that we don't end -- cgit v1.2.3-59-g8ed1b From 0336bdfd7354edfa3db0e01675b5df224516e3e9 Mon Sep 17 00:00:00 2001 From: Mike Leach Date: Thu, 16 Jul 2020 11:57:43 -0600 Subject: coresight: Add default sink selection to CoreSight base Adds a method to select a suitable sink connected to a given source. In cases where no sink is defined, the coresight_find_default_sink routine can search from a given source, through the child connections until a suitable sink is found. The suitability is defined in by the sink coresight_dev_subtype on the CoreSight device, and the distance from the source by counting connections. Higher value subtype is preferred - where these are equal, shorter distance from source is used as a tie-break. This allows for default sink to be discovered were none is specified (e.g. perf command line) Signed-off-by: Mike Leach Suggested-by: Suzuki K Poulose Reviewed-by: Leo Yan Reviewed-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier Link: https://lore.kernel.org/r/20200716175746.3338735-15-mathieu.poirier@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-priv.h | 2 + drivers/hwtracing/coresight/coresight.c | 166 +++++++++++++++++++++++++++ include/linux/coresight.h | 3 + 3 files changed, 171 insertions(+) diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 36c943ae94d5..f2dc625ea585 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -150,6 +150,8 @@ int coresight_enable_path(struct list_head *path, u32 mode, void *sink_data); struct coresight_device *coresight_get_sink(struct list_head *path); struct coresight_device *coresight_get_enabled_sink(bool reset); struct coresight_device *coresight_get_sink_by_id(u32 id); +struct coresight_device * +coresight_find_default_sink(struct coresight_device *csdev); struct list_head *coresight_build_path(struct coresight_device *csdev, struct coresight_device *sink); void coresight_release_path(struct list_head *path); diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index f3efbb3b2b4d..e9c90f2de34a 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -769,6 +769,171 @@ void coresight_release_path(struct list_head *path) path = NULL; } +/* return true if the device is a suitable type for a default sink */ +static inline bool coresight_is_def_sink_type(struct coresight_device *csdev) +{ + /* sink & correct subtype */ + if (((csdev->type == CORESIGHT_DEV_TYPE_SINK) || + (csdev->type == CORESIGHT_DEV_TYPE_LINKSINK)) && + (csdev->subtype.sink_subtype >= CORESIGHT_DEV_SUBTYPE_SINK_BUFFER)) + return true; + return false; +} + +/** + * coresight_select_best_sink - return the best sink for use as default from + * the two provided. + * + * @sink: current best sink. + * @depth: search depth where current sink was found. + * @new_sink: new sink for comparison with current sink. + * @new_depth: search depth where new sink was found. + * + * Sinks prioritised according to coresight_dev_subtype_sink, with only + * subtypes CORESIGHT_DEV_SUBTYPE_SINK_BUFFER or higher being used. + * + * Where two sinks of equal priority are found, the sink closest to the + * source is used (smallest search depth). + * + * return @new_sink & update @depth if better than @sink, else return @sink. + */ +static struct coresight_device * +coresight_select_best_sink(struct coresight_device *sink, int *depth, + struct coresight_device *new_sink, int new_depth) +{ + bool update = false; + + if (!sink) { + /* first found at this level */ + update = true; + } else if (new_sink->subtype.sink_subtype > + sink->subtype.sink_subtype) { + /* found better sink */ + update = true; + } else if ((new_sink->subtype.sink_subtype == + sink->subtype.sink_subtype) && + (*depth > new_depth)) { + /* found same but closer sink */ + update = true; + } + + if (update) + *depth = new_depth; + return update ? new_sink : sink; +} + +/** + * coresight_find_sink - recursive function to walk trace connections from + * source to find a suitable default sink. + * + * @csdev: source / current device to check. + * @depth: [in] search depth of calling dev, [out] depth of found sink. + * + * This will walk the connection path from a source (ETM) till a suitable + * sink is encountered and return that sink to the original caller. + * + * If current device is a plain sink return that & depth, otherwise recursively + * call child connections looking for a sink. Select best possible using + * coresight_select_best_sink. + * + * return best sink found, or NULL if not found at this node or child nodes. + */ +static struct coresight_device * +coresight_find_sink(struct coresight_device *csdev, int *depth) +{ + int i, curr_depth = *depth + 1, found_depth = 0; + struct coresight_device *found_sink = NULL; + + if (coresight_is_def_sink_type(csdev)) { + found_depth = curr_depth; + found_sink = csdev; + if (csdev->type == CORESIGHT_DEV_TYPE_SINK) + goto return_def_sink; + /* look past LINKSINK for something better */ + } + + /* + * Not a sink we want - or possible child sink may be better. + * recursively explore each port found on this element. + */ + for (i = 0; i < csdev->pdata->nr_outport; i++) { + struct coresight_device *child_dev, *sink = NULL; + int child_depth = curr_depth; + + child_dev = csdev->pdata->conns[i].child_dev; + if (child_dev) + sink = coresight_find_sink(child_dev, &child_depth); + + if (sink) + found_sink = coresight_select_best_sink(found_sink, + &found_depth, + sink, + child_depth); + } + +return_def_sink: + /* return found sink and depth */ + if (found_sink) + *depth = found_depth; + return found_sink; +} + +/** + * coresight_find_default_sink: Find a sink suitable for use as a + * default sink. + * + * @csdev: starting source to find a connected sink. + * + * Walks connections graph looking for a suitable sink to enable for the + * supplied source. Uses CoreSight device subtypes and distance from source + * to select the best sink. + * + * If a sink is found, then the default sink for this device is set and + * will be automatically used in future. + * + * Used in cases where the CoreSight user (perf / sysfs) has not selected a + * sink. + */ +struct coresight_device * +coresight_find_default_sink(struct coresight_device *csdev) +{ + int depth = 0; + + /* look for a default sink if we have not found for this device */ + if (!csdev->def_sink) + csdev->def_sink = coresight_find_sink(csdev, &depth); + return csdev->def_sink; +} + +static int coresight_remove_sink_ref(struct device *dev, void *data) +{ + struct coresight_device *sink = data; + struct coresight_device *source = to_coresight_device(dev); + + if (source->def_sink == sink) + source->def_sink = NULL; + return 0; +} + +/** + * coresight_clear_default_sink: Remove all default sink references to the + * supplied sink. + * + * If supplied device is a sink, then check all the bus devices and clear + * out all the references to this sink from the coresight_device def_sink + * parameter. + * + * @csdev: coresight sink - remove references to this from all sources. + */ +static void coresight_clear_default_sink(struct coresight_device *csdev) +{ + if ((csdev->type == CORESIGHT_DEV_TYPE_SINK) || + (csdev->type == CORESIGHT_DEV_TYPE_LINKSINK)) { + bus_for_each_dev(&coresight_bustype, NULL, csdev, + coresight_remove_sink_ref); + } +} + /** coresight_validate_source - make sure a source has the right credentials * @csdev: the device structure for a source. * @function: the function this was called from. @@ -1358,6 +1523,7 @@ void coresight_unregister(struct coresight_device *csdev) etm_perf_del_symlink_sink(csdev); /* Remove references of that device in the topology */ coresight_remove_conns(csdev); + coresight_clear_default_sink(csdev); coresight_release_platform_data(csdev, csdev->pdata); device_unregister(&csdev->dev); } diff --git a/include/linux/coresight.h b/include/linux/coresight.h index 84dc695e87d4..58fffdecdbfd 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -48,6 +48,7 @@ enum coresight_dev_subtype_sink { CORESIGHT_DEV_SUBTYPE_SINK_NONE, CORESIGHT_DEV_SUBTYPE_SINK_PORT, CORESIGHT_DEV_SUBTYPE_SINK_BUFFER, + CORESIGHT_DEV_SUBTYPE_SINK_SYSMEM, }; enum coresight_dev_subtype_link { @@ -182,6 +183,7 @@ struct coresight_sysfs_link { * happens when a source has been selected and a path is enabled * from source to that sink. * @ea: Device attribute for sink representation under PMU directory. + * @def_sink: cached reference to default sink found for this device. * @ect_dev: Associated cross trigger device. Not part of the trace data * path or connections. * @nr_links: number of sysfs links created to other components from this @@ -200,6 +202,7 @@ struct coresight_device { /* sink specific fields */ bool activated; /* true only if a sink is part of a path */ struct dev_ext_attribute *ea; + struct coresight_device *def_sink; /* cross trigger handling */ struct coresight_device *ect_dev; /* sysfs links between components */ -- cgit v1.2.3-59-g8ed1b From 24497d387cdbd231c6a9de2303fb74ab951bfe98 Mon Sep 17 00:00:00 2001 From: Mike Leach Date: Thu, 16 Jul 2020 11:57:44 -0600 Subject: coresight: tmc: Update sink types for default selection An additional sink subtype is added to differentiate ETB/ETF buffer sinks and ETR type system memory sinks. This allows the prioritised selection of default sinks. Signed-off-by: Mike Leach Reviewed-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier Link: https://lore.kernel.org/r/20200716175746.3338735-16-mathieu.poirier@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-tmc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index b13ce0daa572..7040d583bed9 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -484,7 +484,7 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id) break; case TMC_CONFIG_TYPE_ETR: desc.type = CORESIGHT_DEV_TYPE_SINK; - desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER; + desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_SYSMEM; desc.ops = &tmc_etr_cs_ops; ret = tmc_etr_setup_caps(dev, devid, coresight_get_uci_data(id)); @@ -496,6 +496,7 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id) break; case TMC_CONFIG_TYPE_ETF: desc.type = CORESIGHT_DEV_TYPE_LINKSINK; + desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER; desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_FIFO; desc.ops = &tmc_etf_cs_ops; dev_list = &etf_devs; -- cgit v1.2.3-59-g8ed1b From bbfb8f3e4e3f10c7b0b76f9159b8a9608abdea91 Mon Sep 17 00:00:00 2001 From: Mike Leach Date: Thu, 16 Jul 2020 11:57:45 -0600 Subject: coresight: etm: perf: Add default sink selection to etm perf Add default sink selection to the perf trace handling in the etm driver. Uses the select default sink infrastructure to select a sink for the perf session, if no other sink is specified. Signed-off-by: Mike Leach Reviewed-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier Link: https://lore.kernel.org/r/20200716175746.3338735-17-mathieu.poirier@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etm-perf.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c index 84f1dcb69827..1a3169e69bb1 100644 --- a/drivers/hwtracing/coresight/coresight-etm-perf.c +++ b/drivers/hwtracing/coresight/coresight-etm-perf.c @@ -226,9 +226,6 @@ static void *etm_setup_aux(struct perf_event *event, void **pages, sink = coresight_get_enabled_sink(true); } - if (!sink) - goto err; - mask = &event_data->mask; /* @@ -253,6 +250,16 @@ static void *etm_setup_aux(struct perf_event *event, void **pages, continue; } + /* + * No sink provided - look for a default sink for one of the + * devices. At present we only support topology where all CPUs + * use the same sink [N:1], so only need to find one sink. The + * coresight_build_path later will remove any CPU that does not + * attach to the sink, or if we have not found a sink. + */ + if (!sink) + sink = coresight_find_default_sink(csdev); + /* * Building a path doesn't enable it, it simply builds a * list of devices from source to sink that can be @@ -267,6 +274,10 @@ static void *etm_setup_aux(struct perf_event *event, void **pages, *etm_event_cpu_path_ptr(event_data, cpu) = path; } + /* no sink found for any CPU - cannot trace */ + if (!sink) + goto err; + /* If we don't have any CPUs ready for tracing, abort */ cpu = cpumask_first(mask); if (cpu >= nr_cpu_ids) -- cgit v1.2.3-59-g8ed1b From 342c8a1d1d9e418d32fa02d635cf96989f9a986e Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Thu, 16 Jul 2020 11:57:46 -0600 Subject: coresight: etm4x: Fix save/restore during cpu idle The ETM state save/restore incorrectly reads/writes some of the 64bit registers (e.g, address comparators, vmid/cid comparators etc.) using 32bit accesses. Ensure we use the appropriate width accessors for the registers. Fixes: f188b5e76aae ("coresight: etm4x: Save/restore state across CPU low power states") Cc: Mathieu Poirier Cc: Mike Leach Signed-off-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier Link: https://lore.kernel.org/r/20200716175746.3338735-18-mathieu.poirier@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etm4x.c | 16 ++++++++-------- drivers/hwtracing/coresight/coresight-etm4x.h | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 2d732af8b3e7..6d7d2169bfb2 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -1206,8 +1206,8 @@ static int etm4_cpu_save(struct etmv4_drvdata *drvdata) } for (i = 0; i < drvdata->nr_addr_cmp * 2; i++) { - state->trcacvr[i] = readl(drvdata->base + TRCACVRn(i)); - state->trcacatr[i] = readl(drvdata->base + TRCACATRn(i)); + state->trcacvr[i] = readq(drvdata->base + TRCACVRn(i)); + state->trcacatr[i] = readq(drvdata->base + TRCACATRn(i)); } /* @@ -1218,10 +1218,10 @@ static int etm4_cpu_save(struct etmv4_drvdata *drvdata) */ for (i = 0; i < drvdata->numcidc; i++) - state->trccidcvr[i] = readl(drvdata->base + TRCCIDCVRn(i)); + state->trccidcvr[i] = readq(drvdata->base + TRCCIDCVRn(i)); for (i = 0; i < drvdata->numvmidc; i++) - state->trcvmidcvr[i] = readl(drvdata->base + TRCVMIDCVRn(i)); + state->trcvmidcvr[i] = readq(drvdata->base + TRCVMIDCVRn(i)); state->trccidcctlr0 = readl(drvdata->base + TRCCIDCCTLR0); state->trccidcctlr1 = readl(drvdata->base + TRCCIDCCTLR1); @@ -1319,18 +1319,18 @@ static void etm4_cpu_restore(struct etmv4_drvdata *drvdata) } for (i = 0; i < drvdata->nr_addr_cmp * 2; i++) { - writel_relaxed(state->trcacvr[i], + writeq_relaxed(state->trcacvr[i], drvdata->base + TRCACVRn(i)); - writel_relaxed(state->trcacatr[i], + writeq_relaxed(state->trcacatr[i], drvdata->base + TRCACATRn(i)); } for (i = 0; i < drvdata->numcidc; i++) - writel_relaxed(state->trccidcvr[i], + writeq_relaxed(state->trccidcvr[i], drvdata->base + TRCCIDCVRn(i)); for (i = 0; i < drvdata->numvmidc; i++) - writel_relaxed(state->trcvmidcvr[i], + writeq_relaxed(state->trcvmidcvr[i], drvdata->base + TRCVMIDCVRn(i)); writel_relaxed(state->trccidcctlr0, drvdata->base + TRCCIDCCTLR0); diff --git a/drivers/hwtracing/coresight/coresight-etm4x.h b/drivers/hwtracing/coresight/coresight-etm4x.h index 7da022e87218..b8283e1d6d88 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.h +++ b/drivers/hwtracing/coresight/coresight-etm4x.h @@ -334,7 +334,7 @@ struct etmv4_save_state { u64 trcacvr[ETM_MAX_SINGLE_ADDR_CMP]; u64 trcacatr[ETM_MAX_SINGLE_ADDR_CMP]; u64 trccidcvr[ETMv4_MAX_CTXID_CMP]; - u32 trcvmidcvr[ETM_MAX_VMID_CMP]; + u64 trcvmidcvr[ETM_MAX_VMID_CMP]; u32 trccidcctlr0; u32 trccidcctlr1; u32 trcvmidcctlr0; -- cgit v1.2.3-59-g8ed1b From 6c89533deeb36a8f2a7571e13d338edd2c76d473 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 21 Jul 2020 15:40:33 +0100 Subject: dt: update Marvell Armada 38x COMPHY binding Update the Marvell Armada 38x COMPHY binding with an additional optional register pair describing the location of an undocumented system register controlling something to do with the Gigabit Ethernet and COMPHY. There is one bit for each COMPHY lane that may be using the serdes, but exactly what this register does is completely unknown. This register only appears to exist on Armada 38x devices, and not other SoCs using the NETA ethernet block, so it seems logical that it should be part of the COMPHY. This is also how u-boot groups this register; it is dealt with as part of the COMPHY initialisation there. However, at the end of the day, due to the undocumented nature of this register, we can only guess. Signed-off-by: Russell King Acked-by: Rob Herring Link: https://lore.kernel.org/r/E1jxtRZ-0003Ta-4h@rmk-PC.armlinux.org.uk Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/phy/phy-armada38x-comphy.txt | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/phy/phy-armada38x-comphy.txt b/Documentation/devicetree/bindings/phy/phy-armada38x-comphy.txt index ad49e5c01334..8b5a7a28a35b 100644 --- a/Documentation/devicetree/bindings/phy/phy-armada38x-comphy.txt +++ b/Documentation/devicetree/bindings/phy/phy-armada38x-comphy.txt @@ -12,6 +12,13 @@ Required properties: - #address-cells: should be 1. - #size-cells: should be 0. +Optional properties: + +- reg-names: must be "comphy" as the first name, and "conf". +- reg: must contain the comphy register location and length as the first + pair, followed by an optional configuration register address and + length pair. + A sub-node is required for each comphy lane provided by the comphy. Required properties (child nodes): @@ -24,7 +31,8 @@ Example: comphy: phy@18300 { compatible = "marvell,armada-380-comphy"; - reg = <0x18300 0x100>; + reg-names = "comphy", "conf"; + reg = <0x18300 0x100>, <0x18460 4>; #address-cells = <1>; #size-cells = <0>; -- cgit v1.2.3-59-g8ed1b From 1dea06cd643da38931382ebdc151efced201ffad Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 21 Jul 2020 15:40:43 +0100 Subject: phy: armada-38x: fix NETA lockup when repeatedly switching speeds The mvneta hardware appears to lock up in various random ways when repeatedly switching speeds between 1G and 2.5G, which involves reprogramming the COMPHY. It is not entirely clear why this happens, but best guess is that reprogramming the COMPHY glitches mvneta clocks causing the hardware to fail. It seems that rebooting resolves the failure, but not down/up cycling the interface alone. Various other approaches have been tried, such as trying to cleanly power down the COMPHY and then take it back through the power up initialisation, but this does not seem to help. It was finally noticed that u-boot's last step when configuring a COMPHY for "SGMII" mode was to poke at a register described as "GBE_CONFIGURATION_REG", which is undocumented in any external documentation. All that we have is the fact that u-boot sets a bit corresponding to the "SGMII" lane at the end of COMPHY initialisation. Experimentation shows that if we clear this bit prior to changing the speed, and then set it afterwards, mvneta does not suffer this problem on the SolidRun Clearfog when switching speeds between 1G and 2.5G. This problem was found while script-testing phylink. This fix also requires the corresponding change to DT to be effective. See "ARM: dts: armada-38x: fix NETA lockup when repeatedly switching speeds". Fixes: 14dc100b4411 ("phy: armada38x: add common phy support") Signed-off-by: Russell King Reviewed-by: Andrew Lunn Link: https://lore.kernel.org/r/E1jxtRj-0003Tz-CG@rmk-PC.armlinux.org.uk Signed-off-by: Vinod Koul --- drivers/phy/marvell/phy-armada38x-comphy.c | 45 +++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 7 deletions(-) diff --git a/drivers/phy/marvell/phy-armada38x-comphy.c b/drivers/phy/marvell/phy-armada38x-comphy.c index 6960dfd8ad8c..0fe408964334 100644 --- a/drivers/phy/marvell/phy-armada38x-comphy.c +++ b/drivers/phy/marvell/phy-armada38x-comphy.c @@ -41,6 +41,7 @@ struct a38x_comphy_lane { struct a38x_comphy { void __iomem *base; + void __iomem *conf; struct device *dev; struct a38x_comphy_lane lane[MAX_A38X_COMPHY]; }; @@ -54,6 +55,21 @@ static const u8 gbe_mux[MAX_A38X_COMPHY][MAX_A38X_PORTS] = { { 0, 0, 3 }, }; +static void a38x_set_conf(struct a38x_comphy_lane *lane, bool enable) +{ + struct a38x_comphy *priv = lane->priv; + u32 conf; + + if (priv->conf) { + conf = readl_relaxed(priv->conf); + if (enable) + conf |= BIT(lane->port); + else + conf &= ~BIT(lane->port); + writel(conf, priv->conf); + } +} + static void a38x_comphy_set_reg(struct a38x_comphy_lane *lane, unsigned int offset, u32 mask, u32 value) { @@ -97,6 +113,7 @@ static int a38x_comphy_set_mode(struct phy *phy, enum phy_mode mode, int sub) { struct a38x_comphy_lane *lane = phy_get_drvdata(phy); unsigned int gen; + int ret; if (mode != PHY_MODE_ETHERNET) return -EINVAL; @@ -115,13 +132,20 @@ static int a38x_comphy_set_mode(struct phy *phy, enum phy_mode mode, int sub) return -EINVAL; } + a38x_set_conf(lane, false); + a38x_comphy_set_speed(lane, gen, gen); - return a38x_comphy_poll(lane, COMPHY_STAT1, - COMPHY_STAT1_PLL_RDY_TX | - COMPHY_STAT1_PLL_RDY_RX, - COMPHY_STAT1_PLL_RDY_TX | - COMPHY_STAT1_PLL_RDY_RX); + ret = a38x_comphy_poll(lane, COMPHY_STAT1, + COMPHY_STAT1_PLL_RDY_TX | + COMPHY_STAT1_PLL_RDY_RX, + COMPHY_STAT1_PLL_RDY_TX | + COMPHY_STAT1_PLL_RDY_RX); + + if (ret == 0) + a38x_set_conf(lane, true); + + return ret; } static const struct phy_ops a38x_comphy_ops = { @@ -174,14 +198,21 @@ static int a38x_comphy_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(&pdev->dev, res); + base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return PTR_ERR(base); priv->dev = &pdev->dev; priv->base = base; + /* Optional */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "conf"); + if (res) { + priv->conf = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->conf)) + return PTR_ERR(priv->conf); + } + for_each_available_child_of_node(pdev->dev.of_node, child) { struct phy *phy; int ret; -- cgit v1.2.3-59-g8ed1b From 960004121075241330457a3306ad735dd27d76c2 Mon Sep 17 00:00:00 2001 From: Álvaro Fernández Rojas Date: Mon, 20 Jul 2020 15:12:08 +0200 Subject: dt-bindings: phy: add bcm63xx-usbh bindings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Document BCM63xx USBH PHY bindings. Signed-off-by: Álvaro Fernández Rojas Reviewed-by: Florian Fainelli Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20200720131209.1236590-2-noltari@gmail.com Signed-off-by: Vinod Koul --- .../bindings/phy/brcm,bcm63xx-usbh-phy.yaml | 79 ++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/brcm,bcm63xx-usbh-phy.yaml diff --git a/Documentation/devicetree/bindings/phy/brcm,bcm63xx-usbh-phy.yaml b/Documentation/devicetree/bindings/phy/brcm,bcm63xx-usbh-phy.yaml new file mode 100644 index 000000000000..9a2e779e6d38 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/brcm,bcm63xx-usbh-phy.yaml @@ -0,0 +1,79 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/phy/brcm,bcm63xx-usbh-phy.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: BCM63xx USBH PHY + +maintainers: + - Álvaro Fernández Rojas + +properties: + compatible: + enum: + - brcm,bcm6318-usbh-phy + - brcm,bcm6328-usbh-phy + - brcm,bcm6358-usbh-phy + - brcm,bcm6362-usbh-phy + - brcm,bcm6368-usbh-phy + - brcm,bcm63268-usbh-phy + + reg: + maxItems: 1 + + clocks: + minItems: 1 + maxItems: 2 + + clock-names: + minItems: 1 + maxItems: 2 + items: + - const: usbh + - const: usb_ref + + resets: + maxItems: 1 + + "#phy-cells": + const: 1 + +additionalProperties: false + +required: + - compatible + - reg + - clocks + - clock-names + - resets + - "#phy-cells" + +if: + properties: + compatible: + enum: + - brcm,bcm6318-usbh-phy + - brcm,bcm6328-usbh-phy + - brcm,bcm6362-usbh-phy + - brcm,bcm63268-usbh-phy +then: + properties: + power-domains: + maxItems: 1 + required: + - power-domains +else: + properties: + power-domains: false + +examples: + - | + usbh: usb-phy@10001700 { + compatible = "brcm,bcm6368-usbh-phy"; + reg = <0x10001700 0x38>; + clocks = <&periph_clk 15>; + clock-names = "usbh"; + resets = <&periph_rst 12>; + #phy-cells = <1>; + }; -- cgit v1.2.3-59-g8ed1b From 783f6d3dcf35f7b63783756710bebcecb3067f83 Mon Sep 17 00:00:00 2001 From: Álvaro Fernández Rojas Date: Mon, 20 Jul 2020 15:12:09 +0200 Subject: phy: bcm63xx-usbh: Add BCM63xx USBH driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add BCM63xx USBH PHY driver for BMIPS. Signed-off-by: Álvaro Fernández Rojas Signed-off-by: Simon Arlott Link: https://lore.kernel.org/r/20200720131209.1236590-3-noltari@gmail.com Signed-off-by: Vinod Koul --- drivers/phy/broadcom/Kconfig | 8 + drivers/phy/broadcom/Makefile | 1 + drivers/phy/broadcom/phy-bcm63xx-usbh.c | 457 ++++++++++++++++++++++++++++++++ 3 files changed, 466 insertions(+) create mode 100644 drivers/phy/broadcom/phy-bcm63xx-usbh.c diff --git a/drivers/phy/broadcom/Kconfig b/drivers/phy/broadcom/Kconfig index b29f11c19155..a1f1a9c90d0d 100644 --- a/drivers/phy/broadcom/Kconfig +++ b/drivers/phy/broadcom/Kconfig @@ -2,6 +2,14 @@ # # Phy drivers for Broadcom platforms # +config PHY_BCM63XX_USBH + tristate "BCM63xx USBH PHY driver" + depends on BMIPS_GENERIC || COMPILE_TEST + select GENERIC_PHY + help + Enable this to support the BCM63xx USBH PHY driver. + If unsure, say N. + config PHY_CYGNUS_PCIE tristate "Broadcom Cygnus PCIe PHY driver" depends on OF && (ARCH_BCM_CYGNUS || COMPILE_TEST) diff --git a/drivers/phy/broadcom/Makefile b/drivers/phy/broadcom/Makefile index c78de546135c..7024127f86ad 100644 --- a/drivers/phy/broadcom/Makefile +++ b/drivers/phy/broadcom/Makefile @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_PHY_BCM63XX_USBH) += phy-bcm63xx-usbh.o obj-$(CONFIG_PHY_CYGNUS_PCIE) += phy-bcm-cygnus-pcie.o obj-$(CONFIG_BCM_KONA_USB2_PHY) += phy-bcm-kona-usb2.o obj-$(CONFIG_PHY_BCM_NS_USB2) += phy-bcm-ns-usb2.o diff --git a/drivers/phy/broadcom/phy-bcm63xx-usbh.c b/drivers/phy/broadcom/phy-bcm63xx-usbh.c new file mode 100644 index 000000000000..6c05ba8b08be --- /dev/null +++ b/drivers/phy/broadcom/phy-bcm63xx-usbh.c @@ -0,0 +1,457 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * BCM6328 USBH PHY Controller Driver + * + * Copyright (C) 2020 Álvaro Fernández Rojas + * Copyright (C) 2015 Simon Arlott + * + * Derived from bcm963xx_4.12L.06B_consumer/kernel/linux/arch/mips/bcm963xx/setup.c: + * Copyright (C) 2002 Broadcom Corporation + * + * Derived from OpenWrt patches: + * Copyright (C) 2013 Jonas Gorski + * Copyright (C) 2013 Florian Fainelli + * Copyright (C) 2008 Maxime Bizon + */ + +#include +#include +#include +#include +#include +#include + +/* USBH control register offsets */ +enum usbh_regs { + USBH_BRT_CONTROL1 = 0, + USBH_BRT_CONTROL2, + USBH_BRT_STATUS1, + USBH_BRT_STATUS2, + USBH_UTMI_CONTROL1, +#define USBH_UC1_DEV_MODE_SEL BIT(0) + USBH_TEST_PORT_CONTROL, + USBH_PLL_CONTROL1, +#define USBH_PLLC_REFCLKSEL_SHIFT 0 +#define USBH_PLLC_REFCLKSEL_MASK (0x3 << USBH_PLLC_REFCLKSEL_SHIFT) +#define USBH_PLLC_CLKSEL_SHIFT 2 +#define USBH_PLLC_CLKSEL_MASK (0x3 << USBH_PLLC_CLKSEL_MASK) +#define USBH_PLLC_XTAL_PWRDWNB BIT(4) +#define USBH_PLLC_PLL_PWRDWNB BIT(5) +#define USBH_PLLC_PLL_CALEN BIT(6) +#define USBH_PLLC_PHYPLL_BYP BIT(7) +#define USBH_PLLC_PLL_RESET BIT(8) +#define USBH_PLLC_PLL_IDDQ_PWRDN BIT(9) +#define USBH_PLLC_PLL_PWRDN_DELAY BIT(10) +#define USBH_6318_PLLC_PLL_SUSPEND_EN BIT(27) +#define USBH_6318_PLLC_PHYPLL_BYP BIT(29) +#define USBH_6318_PLLC_PLL_RESET BIT(30) +#define USBH_6318_PLLC_PLL_IDDQ_PWRDN BIT(31) + USBH_SWAP_CONTROL, +#define USBH_SC_OHCI_DATA_SWAP BIT(0) +#define USBH_SC_OHCI_ENDIAN_SWAP BIT(1) +#define USBH_SC_OHCI_LOGICAL_ADDR_EN BIT(2) +#define USBH_SC_EHCI_DATA_SWAP BIT(3) +#define USBH_SC_EHCI_ENDIAN_SWAP BIT(4) +#define USBH_SC_EHCI_LOGICAL_ADDR_EN BIT(5) +#define USBH_SC_USB_DEVICE_SEL BIT(6) + USBH_GENERIC_CONTROL, +#define USBH_GC_PLL_SUSPEND_EN BIT(1) + USBH_FRAME_ADJUST_VALUE, + USBH_SETUP, +#define USBH_S_IOC BIT(4) +#define USBH_S_IPP BIT(5) + USBH_MDIO, + USBH_MDIO32, + USBH_USB_SIM_CONTROL, +#define USBH_USC_LADDR_SEL BIT(5) + + __USBH_ENUM_SIZE +}; + +struct bcm63xx_usbh_phy_variant { + /* Registers */ + long regs[__USBH_ENUM_SIZE]; + + /* PLLC bits to set/clear for power on */ + u32 power_pllc_clr; + u32 power_pllc_set; + + /* Setup bits to set/clear for power on */ + u32 setup_clr; + u32 setup_set; + + /* Swap Control bits to set */ + u32 swapctl_dev_set; + + /* Test Port Control value to set if non-zero */ + u32 tpc_val; + + /* USB Sim Control bits to set */ + u32 usc_set; + + /* UTMI Control 1 bits to set */ + u32 utmictl1_dev_set; +}; + +struct bcm63xx_usbh_phy { + void __iomem *base; + struct clk *usbh_clk; + struct clk *usb_ref_clk; + struct reset_control *reset; + const struct bcm63xx_usbh_phy_variant *variant; + bool device_mode; +}; + +static const struct bcm63xx_usbh_phy_variant usbh_bcm6318 = { + .regs = { + [USBH_BRT_CONTROL1] = -1, + [USBH_BRT_CONTROL2] = -1, + [USBH_BRT_STATUS1] = -1, + [USBH_BRT_STATUS2] = -1, + [USBH_UTMI_CONTROL1] = 0x2c, + [USBH_TEST_PORT_CONTROL] = 0x1c, + [USBH_PLL_CONTROL1] = 0x04, + [USBH_SWAP_CONTROL] = 0x0c, + [USBH_GENERIC_CONTROL] = -1, + [USBH_FRAME_ADJUST_VALUE] = 0x08, + [USBH_SETUP] = 0x00, + [USBH_MDIO] = 0x14, + [USBH_MDIO32] = 0x18, + [USBH_USB_SIM_CONTROL] = 0x20, + }, + .power_pllc_clr = USBH_6318_PLLC_PLL_IDDQ_PWRDN, + .power_pllc_set = USBH_6318_PLLC_PLL_SUSPEND_EN, + .setup_set = USBH_S_IOC, + .swapctl_dev_set = USBH_SC_USB_DEVICE_SEL, + .usc_set = USBH_USC_LADDR_SEL, + .utmictl1_dev_set = USBH_UC1_DEV_MODE_SEL, +}; + +static const struct bcm63xx_usbh_phy_variant usbh_bcm6328 = { + .regs = { + [USBH_BRT_CONTROL1] = 0x00, + [USBH_BRT_CONTROL2] = 0x04, + [USBH_BRT_STATUS1] = 0x08, + [USBH_BRT_STATUS2] = 0x0c, + [USBH_UTMI_CONTROL1] = 0x10, + [USBH_TEST_PORT_CONTROL] = 0x14, + [USBH_PLL_CONTROL1] = 0x18, + [USBH_SWAP_CONTROL] = 0x1c, + [USBH_GENERIC_CONTROL] = 0x20, + [USBH_FRAME_ADJUST_VALUE] = 0x24, + [USBH_SETUP] = 0x28, + [USBH_MDIO] = 0x2c, + [USBH_MDIO32] = 0x30, + [USBH_USB_SIM_CONTROL] = 0x34, + }, + .setup_set = USBH_S_IOC, + .swapctl_dev_set = USBH_SC_USB_DEVICE_SEL, + .utmictl1_dev_set = USBH_UC1_DEV_MODE_SEL, +}; + +static const struct bcm63xx_usbh_phy_variant usbh_bcm6358 = { + .regs = { + [USBH_BRT_CONTROL1] = -1, + [USBH_BRT_CONTROL2] = -1, + [USBH_BRT_STATUS1] = -1, + [USBH_BRT_STATUS2] = -1, + [USBH_UTMI_CONTROL1] = -1, + [USBH_TEST_PORT_CONTROL] = 0x24, + [USBH_PLL_CONTROL1] = -1, + [USBH_SWAP_CONTROL] = 0x00, + [USBH_GENERIC_CONTROL] = -1, + [USBH_FRAME_ADJUST_VALUE] = -1, + [USBH_SETUP] = -1, + [USBH_MDIO] = -1, + [USBH_MDIO32] = -1, + [USBH_USB_SIM_CONTROL] = -1, + }, + /* + * The magic value comes for the original vendor BSP + * and is needed for USB to work. Datasheet does not + * help, so the magic value is used as-is. + */ + .tpc_val = 0x1c0020, +}; + +static const struct bcm63xx_usbh_phy_variant usbh_bcm6368 = { + .regs = { + [USBH_BRT_CONTROL1] = 0x00, + [USBH_BRT_CONTROL2] = 0x04, + [USBH_BRT_STATUS1] = 0x08, + [USBH_BRT_STATUS2] = 0x0c, + [USBH_UTMI_CONTROL1] = 0x10, + [USBH_TEST_PORT_CONTROL] = 0x14, + [USBH_PLL_CONTROL1] = 0x18, + [USBH_SWAP_CONTROL] = 0x1c, + [USBH_GENERIC_CONTROL] = -1, + [USBH_FRAME_ADJUST_VALUE] = 0x24, + [USBH_SETUP] = 0x28, + [USBH_MDIO] = 0x2c, + [USBH_MDIO32] = 0x30, + [USBH_USB_SIM_CONTROL] = 0x34, + }, + .power_pllc_clr = USBH_PLLC_PLL_IDDQ_PWRDN | USBH_PLLC_PLL_PWRDN_DELAY, + .setup_set = USBH_S_IOC, + .swapctl_dev_set = USBH_SC_USB_DEVICE_SEL, + .utmictl1_dev_set = USBH_UC1_DEV_MODE_SEL, +}; + +static const struct bcm63xx_usbh_phy_variant usbh_bcm63268 = { + .regs = { + [USBH_BRT_CONTROL1] = 0x00, + [USBH_BRT_CONTROL2] = 0x04, + [USBH_BRT_STATUS1] = 0x08, + [USBH_BRT_STATUS2] = 0x0c, + [USBH_UTMI_CONTROL1] = 0x10, + [USBH_TEST_PORT_CONTROL] = 0x14, + [USBH_PLL_CONTROL1] = 0x18, + [USBH_SWAP_CONTROL] = 0x1c, + [USBH_GENERIC_CONTROL] = 0x20, + [USBH_FRAME_ADJUST_VALUE] = 0x24, + [USBH_SETUP] = 0x28, + [USBH_MDIO] = 0x2c, + [USBH_MDIO32] = 0x30, + [USBH_USB_SIM_CONTROL] = 0x34, + }, + .power_pllc_clr = USBH_PLLC_PLL_IDDQ_PWRDN | USBH_PLLC_PLL_PWRDN_DELAY, + .setup_clr = USBH_S_IPP, + .setup_set = USBH_S_IOC, + .swapctl_dev_set = USBH_SC_USB_DEVICE_SEL, + .utmictl1_dev_set = USBH_UC1_DEV_MODE_SEL, +}; + +static inline bool usbh_has_reg(struct bcm63xx_usbh_phy *usbh, int reg) +{ + return (usbh->variant->regs[reg] >= 0); +} + +static inline u32 usbh_readl(struct bcm63xx_usbh_phy *usbh, int reg) +{ + return __raw_readl(usbh->base + usbh->variant->regs[reg]); +} + +static inline void usbh_writel(struct bcm63xx_usbh_phy *usbh, int reg, + u32 value) +{ + __raw_writel(value, usbh->base + usbh->variant->regs[reg]); +} + +static int bcm63xx_usbh_phy_init(struct phy *phy) +{ + struct bcm63xx_usbh_phy *usbh = phy_get_drvdata(phy); + int ret; + + ret = clk_prepare_enable(usbh->usbh_clk); + if (ret) { + dev_err(&phy->dev, "unable to enable usbh clock: %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(usbh->usb_ref_clk); + if (ret) { + dev_err(&phy->dev, "unable to enable usb_ref clock: %d\n", ret); + clk_disable_unprepare(usbh->usbh_clk); + return ret; + } + + ret = reset_control_reset(usbh->reset); + if (ret) { + dev_err(&phy->dev, "unable to reset device: %d\n", ret); + clk_disable_unprepare(usbh->usb_ref_clk); + clk_disable_unprepare(usbh->usbh_clk); + return ret; + } + + /* Configure to work in native CPU endian */ + if (usbh_has_reg(usbh, USBH_SWAP_CONTROL)) { + u32 val = usbh_readl(usbh, USBH_SWAP_CONTROL); + + val |= USBH_SC_EHCI_DATA_SWAP; + val &= ~USBH_SC_EHCI_ENDIAN_SWAP; + + val |= USBH_SC_OHCI_DATA_SWAP; + val &= ~USBH_SC_OHCI_ENDIAN_SWAP; + + if (usbh->device_mode && usbh->variant->swapctl_dev_set) + val |= usbh->variant->swapctl_dev_set; + + usbh_writel(usbh, USBH_SWAP_CONTROL, val); + } + + if (usbh_has_reg(usbh, USBH_SETUP)) { + u32 val = usbh_readl(usbh, USBH_SETUP); + + val |= usbh->variant->setup_set; + val &= ~usbh->variant->setup_clr; + + usbh_writel(usbh, USBH_SETUP, val); + } + + if (usbh_has_reg(usbh, USBH_USB_SIM_CONTROL)) { + u32 val = usbh_readl(usbh, USBH_USB_SIM_CONTROL); + + val |= usbh->variant->usc_set; + + usbh_writel(usbh, USBH_USB_SIM_CONTROL, val); + } + + if (usbh->variant->tpc_val && + usbh_has_reg(usbh, USBH_TEST_PORT_CONTROL)) + usbh_writel(usbh, USBH_TEST_PORT_CONTROL, + usbh->variant->tpc_val); + + if (usbh->device_mode && + usbh_has_reg(usbh, USBH_UTMI_CONTROL1) && + usbh->variant->utmictl1_dev_set) { + u32 val = usbh_readl(usbh, USBH_UTMI_CONTROL1); + + val |= usbh->variant->utmictl1_dev_set; + + usbh_writel(usbh, USBH_UTMI_CONTROL1, val); + } + + return 0; +} + +static int bcm63xx_usbh_phy_power_on(struct phy *phy) +{ + struct bcm63xx_usbh_phy *usbh = phy_get_drvdata(phy); + + if (usbh_has_reg(usbh, USBH_PLL_CONTROL1)) { + u32 val = usbh_readl(usbh, USBH_PLL_CONTROL1); + + val |= usbh->variant->power_pllc_set; + val &= ~usbh->variant->power_pllc_clr; + + usbh_writel(usbh, USBH_PLL_CONTROL1, val); + } + + return 0; +} + +static int bcm63xx_usbh_phy_power_off(struct phy *phy) +{ + struct bcm63xx_usbh_phy *usbh = phy_get_drvdata(phy); + + if (usbh_has_reg(usbh, USBH_PLL_CONTROL1)) { + u32 val = usbh_readl(usbh, USBH_PLL_CONTROL1); + + val &= ~usbh->variant->power_pllc_set; + val |= usbh->variant->power_pllc_clr; + + usbh_writel(usbh, USBH_PLL_CONTROL1, val); + } + + return 0; +} + +static int bcm63xx_usbh_phy_exit(struct phy *phy) +{ + struct bcm63xx_usbh_phy *usbh = phy_get_drvdata(phy); + + clk_disable_unprepare(usbh->usbh_clk); + clk_disable_unprepare(usbh->usb_ref_clk); + + return 0; +} + +static const struct phy_ops bcm63xx_usbh_phy_ops = { + .exit = bcm63xx_usbh_phy_exit, + .init = bcm63xx_usbh_phy_init, + .power_off = bcm63xx_usbh_phy_power_off, + .power_on = bcm63xx_usbh_phy_power_on, + .owner = THIS_MODULE, +}; + +static struct phy *bcm63xx_usbh_phy_xlate(struct device *dev, + struct of_phandle_args *args) +{ + struct bcm63xx_usbh_phy *usbh = dev_get_drvdata(dev); + + usbh->device_mode = !!args->args[0]; + + return of_phy_simple_xlate(dev, args); +} + +static int __init bcm63xx_usbh_phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct bcm63xx_usbh_phy *usbh; + const struct bcm63xx_usbh_phy_variant *variant; + struct phy *phy; + struct phy_provider *phy_provider; + + usbh = devm_kzalloc(dev, sizeof(*usbh), GFP_KERNEL); + if (!usbh) + return -ENOMEM; + + variant = device_get_match_data(dev); + if (!variant) + return -EINVAL; + usbh->variant = variant; + + usbh->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(usbh->base)) + return PTR_ERR(usbh->base); + + usbh->reset = devm_reset_control_get_exclusive(dev, NULL); + if (IS_ERR(usbh->reset)) { + if (PTR_ERR(usbh->reset) != -EPROBE_DEFER) + dev_err(dev, "failed to get reset\n"); + return PTR_ERR(usbh->reset); + } + + usbh->usbh_clk = devm_clk_get_optional(dev, "usbh"); + if (IS_ERR(usbh->usbh_clk)) + return PTR_ERR(usbh->usbh_clk); + + usbh->usb_ref_clk = devm_clk_get_optional(dev, "usb_ref"); + if (IS_ERR(usbh->usb_ref_clk)) + return PTR_ERR(usbh->usb_ref_clk); + + phy = devm_phy_create(dev, NULL, &bcm63xx_usbh_phy_ops); + if (IS_ERR(phy)) { + dev_err(dev, "failed to create PHY\n"); + return PTR_ERR(phy); + } + + platform_set_drvdata(pdev, usbh); + phy_set_drvdata(phy, usbh); + + phy_provider = devm_of_phy_provider_register(dev, + bcm63xx_usbh_phy_xlate); + if (IS_ERR(phy_provider)) { + dev_err(dev, "failed to register PHY provider\n"); + return PTR_ERR(phy_provider); + } + + dev_dbg(dev, "Registered BCM63xx USB PHY driver\n"); + + return 0; +} + +static const struct of_device_id bcm63xx_usbh_phy_ids[] __initconst = { + { .compatible = "brcm,bcm6318-usbh-phy", .data = &usbh_bcm6318 }, + { .compatible = "brcm,bcm6328-usbh-phy", .data = &usbh_bcm6328 }, + { .compatible = "brcm,bcm6358-usbh-phy", .data = &usbh_bcm6358 }, + { .compatible = "brcm,bcm6362-usbh-phy", .data = &usbh_bcm6368 }, + { .compatible = "brcm,bcm6368-usbh-phy", .data = &usbh_bcm6368 }, + { .compatible = "brcm,bcm63268-usbh-phy", .data = &usbh_bcm63268 }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, bcm63xx_usbh_phy_ids); + +static struct platform_driver bcm63xx_usbh_phy_driver __refdata = { + .driver = { + .name = "bcm63xx-usbh-phy", + .of_match_table = bcm63xx_usbh_phy_ids, + }, + .probe = bcm63xx_usbh_phy_probe, +}; +module_platform_driver(bcm63xx_usbh_phy_driver); + +MODULE_DESCRIPTION("BCM63xx USBH PHY driver"); +MODULE_AUTHOR("Álvaro Fernández Rojas "); +MODULE_AUTHOR("Simon Arlott"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From 3d7b0ca5300bd01b176f2b4c10e173db802560d8 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 21 Jul 2020 16:06:13 +0100 Subject: phy: qualcomm: fix setting of tx_deamp_3_5db when device property read fails Currently when reading of the device property for "qcom,tx-deamp_3_5db" fails the default is being assigned incorrectly to phy_dwc3->rx_eq. This looks like a copy-n-paste error and in fact should be assigning the default instead to phy_dwc3->tx_deamp_3_5db Addresses-Coverity: ("Copy-paste error") Fixes: ef19b117b834 ("phy: qualcomm: add qcom ipq806x dwc usb phy driver") Signed-off-by: Colin Ian King Link: https://lore.kernel.org/r/20200721150613.416876-1-colin.king@canonical.com Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-ipq806x-usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/qualcomm/phy-qcom-ipq806x-usb.c b/drivers/phy/qualcomm/phy-qcom-ipq806x-usb.c index a7241bf110d7..71f257b4a7f5 100644 --- a/drivers/phy/qualcomm/phy-qcom-ipq806x-usb.c +++ b/drivers/phy/qualcomm/phy-qcom-ipq806x-usb.c @@ -531,7 +531,7 @@ static int qcom_ipq806x_usb_phy_probe(struct platform_device *pdev) if (device_property_read_u32(&pdev->dev, "qcom,tx-deamp_3_5db", &phy_dwc3->tx_deamp_3_5db)) - phy_dwc3->rx_eq = SSPHY_TX_DEEMPH_3_5DB; + phy_dwc3->tx_deamp_3_5db = SSPHY_TX_DEEMPH_3_5DB; if (device_property_read_u32(&pdev->dev, "qcom,mpll", &phy_dwc3->mpll)) phy_dwc3->mpll = SSPHY_MPLL_VALUE; -- cgit v1.2.3-59-g8ed1b From ff36dc6e9654ae511039300b102e8f3d30814859 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 21 Jul 2020 16:23:32 -0500 Subject: misc: rtsx: Use pcie_capability_clear_and_set_word() for PCI_EXP_LNKCTL Instead of using the driver-specific rtsx_pci_write_config_byte() to update the PCIe Link Control Register, use pcie_capability_write_word() like the rest of the kernel does. This makes it easier to maintain ASPM across the PCI core and drivers. No functional change intended. I missed this when doing 3d1e7aa80d1c ("misc: rtsx: Use pcie_capability_clear_and_set_word() for PCI_EXP_LNKCTL"). Signed-off-by: Bjorn Helgaas Acked-by: Arnd Bergmann Link: https://lore.kernel.org/r/20200721212336.1159079-2-helgaas@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/cardreader/rtsx_pcr.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/misc/cardreader/rtsx_pcr.c b/drivers/misc/cardreader/rtsx_pcr.c index 5ff690d2e9a9..6d76929f31f5 100644 --- a/drivers/misc/cardreader/rtsx_pcr.c +++ b/drivers/misc/cardreader/rtsx_pcr.c @@ -1246,6 +1246,7 @@ int rtsx_ms_power_off_card3v3(struct rtsx_pcr *pcr) static int rtsx_pci_init_hw(struct rtsx_pcr *pcr) { + struct pci_dev *pdev = pcr->pci; int err; if (PCI_PID(pcr) == PID_5228) @@ -1347,7 +1348,8 @@ static int rtsx_pci_init_hw(struct rtsx_pcr *pcr) rtsx_pci_init_ocp(pcr); /* Enable clk_request_n to enable clock power management */ - rtsx_pci_write_config_byte(pcr, pcr->pcie_cap + PCI_EXP_LNKCTL + 1, 1); + pcie_capability_write_word(pdev, PCI_EXP_LNKCTL, + PCI_EXP_LNKCTL_CLKREQ_EN); /* Enter L1 when host tx idle */ rtsx_pci_write_config_byte(pcr, 0x70F, 0x5B); -- cgit v1.2.3-59-g8ed1b From df746b3f079c31db7350b282c86e9004fa1a88df Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 21 Jul 2020 16:23:33 -0500 Subject: misc: rtsx: Remove unused pcie_cap There are no more uses of struct rtsx_pcr.pcie_cap. Remove it. Signed-off-by: Bjorn Helgaas Acked-by: Arnd Bergmann Link: https://lore.kernel.org/r/20200721212336.1159079-3-helgaas@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/cardreader/rtsx_pcr.c | 1 - include/linux/rtsx_pci.h | 1 - 2 files changed, 2 deletions(-) diff --git a/drivers/misc/cardreader/rtsx_pcr.c b/drivers/misc/cardreader/rtsx_pcr.c index 6d76929f31f5..0c0f1dd6f00f 100644 --- a/drivers/misc/cardreader/rtsx_pcr.c +++ b/drivers/misc/cardreader/rtsx_pcr.c @@ -1253,7 +1253,6 @@ static int rtsx_pci_init_hw(struct rtsx_pcr *pcr) rtsx_pci_write_register(pcr, RTS5228_LDO1_CFG1, RTS5228_LDO1_SR_TIME_MASK, RTS5228_LDO1_SR_0_5); - pcr->pcie_cap = pci_find_capability(pcr->pci, PCI_CAP_ID_EXP); rtsx_pci_writel(pcr, RTSX_HCBAR, pcr->host_cmds_addr); rtsx_pci_enable_bus_int(pcr); diff --git a/include/linux/rtsx_pci.h b/include/linux/rtsx_pci.h index 27a6ea82aeea..4ff7b221f36e 100644 --- a/include/linux/rtsx_pci.h +++ b/include/linux/rtsx_pci.h @@ -1166,7 +1166,6 @@ struct rtsx_hw_param { struct rtsx_pcr { struct pci_dev *pci; unsigned int id; - int pcie_cap; struct rtsx_cr_option option; struct rtsx_hw_param hw_param; -- cgit v1.2.3-59-g8ed1b From 22bf3251d7b7da0339f41ec27f2c3d4e0ec02255 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 21 Jul 2020 16:23:34 -0500 Subject: misc: rtsx: Remove rtsx_pci_read/write_config() wrappers rtsx_pci_read_config_dword() and similar wrappers around the PCI config accessors add very little value, and they obscure the fact that often we are accessing standard PCI registers that should be coordinated with the PCI core. Remove the wrappers and use the PCI config accessors directly. No functional change intended. Signed-off-by: Bjorn Helgaas Acked-by: Arnd Bergmann Link: https://lore.kernel.org/r/20200721212336.1159079-4-helgaas@kernel.org [ fixed up some other instances as original patch was based on old tree - gregkh Signed-off-by: Greg Kroah-Hartman --- drivers/misc/cardreader/rtl8411.c | 8 +++++--- drivers/misc/cardreader/rts5209.c | 5 +++-- drivers/misc/cardreader/rts5227.c | 5 +++-- drivers/misc/cardreader/rts5228.c | 8 +++++--- drivers/misc/cardreader/rts5229.c | 5 +++-- drivers/misc/cardreader/rts5249.c | 12 +++++++----- drivers/misc/cardreader/rts5260.c | 10 ++++++---- drivers/misc/cardreader/rts5261.c | 19 +++++++++++-------- drivers/misc/cardreader/rtsx_pcr.c | 2 +- include/linux/rtsx_pci.h | 12 ------------ 10 files changed, 44 insertions(+), 42 deletions(-) diff --git a/drivers/misc/cardreader/rtl8411.c b/drivers/misc/cardreader/rtl8411.c index 489ebe907688..a07674ed0596 100644 --- a/drivers/misc/cardreader/rtl8411.c +++ b/drivers/misc/cardreader/rtl8411.c @@ -37,10 +37,11 @@ static int rtl8411b_is_qfn48(struct rtsx_pcr *pcr) static void rtl8411_fetch_vendor_settings(struct rtsx_pcr *pcr) { + struct pci_dev *pdev = pcr->pci; u32 reg1 = 0; u8 reg3 = 0; - rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG1, ®1); + pci_read_config_dword(pdev, PCR_SETTING_REG1, ®1); pcr_dbg(pcr, "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG1, reg1); if (!rtsx_vendor_setting_valid(reg1)) @@ -52,16 +53,17 @@ static void rtl8411_fetch_vendor_settings(struct rtsx_pcr *pcr) pcr->card_drive_sel &= 0x3F; pcr->card_drive_sel |= rtsx_reg_to_card_drive_sel(reg1); - rtsx_pci_read_config_byte(pcr, PCR_SETTING_REG3, ®3); + pci_read_config_byte(pdev, PCR_SETTING_REG3, ®3); pcr_dbg(pcr, "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG3, reg3); pcr->sd30_drive_sel_3v3 = rtl8411_reg_to_sd30_drive_sel_3v3(reg3); } static void rtl8411b_fetch_vendor_settings(struct rtsx_pcr *pcr) { + struct pci_dev *pdev = pcr->pci; u32 reg = 0; - rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG1, ®); + pci_read_config_dword(pdev, PCR_SETTING_REG1, ®); pcr_dbg(pcr, "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG1, reg); if (!rtsx_vendor_setting_valid(reg)) diff --git a/drivers/misc/cardreader/rts5209.c b/drivers/misc/cardreader/rts5209.c index 659056164b21..39a6a7ecc32e 100644 --- a/drivers/misc/cardreader/rts5209.c +++ b/drivers/misc/cardreader/rts5209.c @@ -23,9 +23,10 @@ static u8 rts5209_get_ic_version(struct rtsx_pcr *pcr) static void rts5209_fetch_vendor_settings(struct rtsx_pcr *pcr) { + struct pci_dev *pdev = pcr->pci; u32 reg; - rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG1, ®); + pci_read_config_dword(pdev, PCR_SETTING_REG1, ®); pcr_dbg(pcr, "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG1, reg); if (rts5209_vendor_setting1_valid(reg)) { @@ -34,7 +35,7 @@ static void rts5209_fetch_vendor_settings(struct rtsx_pcr *pcr) pcr->aspm_en = rts5209_reg_to_aspm(reg); } - rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG2, ®); + pci_read_config_dword(pdev, PCR_SETTING_REG2, ®); pcr_dbg(pcr, "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG2, reg); if (rts5209_vendor_setting2_valid(reg)) { diff --git a/drivers/misc/cardreader/rts5227.c b/drivers/misc/cardreader/rts5227.c index 3a9467aaa435..f5f392ddf3d6 100644 --- a/drivers/misc/cardreader/rts5227.c +++ b/drivers/misc/cardreader/rts5227.c @@ -56,9 +56,10 @@ static void rts5227_fill_driving(struct rtsx_pcr *pcr, u8 voltage) static void rts5227_fetch_vendor_settings(struct rtsx_pcr *pcr) { + struct pci_dev *pdev = pcr->pci; u32 reg; - rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG1, ®); + pci_read_config_dword(pdev, PCR_SETTING_REG1, ®); pcr_dbg(pcr, "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG1, reg); if (!rtsx_vendor_setting_valid(reg)) @@ -69,7 +70,7 @@ static void rts5227_fetch_vendor_settings(struct rtsx_pcr *pcr) pcr->card_drive_sel &= 0x3F; pcr->card_drive_sel |= rtsx_reg_to_card_drive_sel(reg); - rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG2, ®); + pci_read_config_dword(pdev, PCR_SETTING_REG2, ®); pcr_dbg(pcr, "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG2, reg); pcr->sd30_drive_sel_3v3 = rtsx_reg_to_sd30_drive_sel_3v3(reg); if (rtsx_reg_check_reverse_socket(reg)) diff --git a/drivers/misc/cardreader/rts5228.c b/drivers/misc/cardreader/rts5228.c index 99aff7cd0a93..448929829de4 100644 --- a/drivers/misc/cardreader/rts5228.c +++ b/drivers/misc/cardreader/rts5228.c @@ -60,9 +60,11 @@ static void rts5228_fill_driving(struct rtsx_pcr *pcr, u8 voltage) static void rtsx5228_fetch_vendor_settings(struct rtsx_pcr *pcr) { + struct pci_dev *pdev = pcr->pci; u32 reg; + /* 0x724~0x727 */ - rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG1, ®); + pci_read_config_dword(pdev, PCR_SETTING_REG1, ®); pcr_dbg(pcr, "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG1, reg); if (!rtsx_vendor_setting_valid(reg)) { @@ -73,7 +75,7 @@ static void rtsx5228_fetch_vendor_settings(struct rtsx_pcr *pcr) pcr->aspm_en = rtsx_reg_to_aspm(reg); /* 0x814~0x817 */ - rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG2, ®); + pci_read_config_dword(pdev, PCR_SETTING_REG2, ®); pcr_dbg(pcr, "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG2, reg); pcr->rtd3_en = rtsx_reg_to_rtd3(reg); @@ -380,7 +382,7 @@ static void rts5228_init_from_cfg(struct rtsx_pcr *pcr) u32 lval; struct rtsx_cr_option *option = &pcr->option; - rtsx_pci_read_config_dword(pcr, PCR_ASPM_SETTING_REG1, &lval); + pci_read_config_dword(pcr->pci, PCR_ASPM_SETTING_REG1, &lval); if (0 == (lval & 0x0F)) diff --git a/drivers/misc/cardreader/rts5229.c b/drivers/misc/cardreader/rts5229.c index 9f080a32ef50..89e6f124ca5c 100644 --- a/drivers/misc/cardreader/rts5229.c +++ b/drivers/misc/cardreader/rts5229.c @@ -23,9 +23,10 @@ static u8 rts5229_get_ic_version(struct rtsx_pcr *pcr) static void rts5229_fetch_vendor_settings(struct rtsx_pcr *pcr) { + struct pci_dev *pdev = pcr->pci; u32 reg; - rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG1, ®); + pci_read_config_dword(pdev, PCR_SETTING_REG1, ®); pcr_dbg(pcr, "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG1, reg); if (!rtsx_vendor_setting_valid(reg)) @@ -37,7 +38,7 @@ static void rts5229_fetch_vendor_settings(struct rtsx_pcr *pcr) pcr->card_drive_sel &= 0x3F; pcr->card_drive_sel |= rtsx_reg_to_card_drive_sel(reg); - rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG2, ®); + pci_read_config_dword(pdev, PCR_SETTING_REG2, ®); pcr_dbg(pcr, "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG2, reg); pcr->sd30_drive_sel_3v3 = map_sd_drive(rtsx_reg_to_sd30_drive_sel_3v3(reg)); diff --git a/drivers/misc/cardreader/rts5249.c b/drivers/misc/cardreader/rts5249.c index 6c6c9e95a29f..665472d05993 100644 --- a/drivers/misc/cardreader/rts5249.c +++ b/drivers/misc/cardreader/rts5249.c @@ -55,9 +55,10 @@ static void rts5249_fill_driving(struct rtsx_pcr *pcr, u8 voltage) static void rtsx_base_fetch_vendor_settings(struct rtsx_pcr *pcr) { + struct pci_dev *pdev = pcr->pci; u32 reg; - rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG1, ®); + pci_read_config_dword(pdev, PCR_SETTING_REG1, ®); pcr_dbg(pcr, "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG1, reg); if (!rtsx_vendor_setting_valid(reg)) { @@ -70,7 +71,7 @@ static void rtsx_base_fetch_vendor_settings(struct rtsx_pcr *pcr) pcr->card_drive_sel &= 0x3F; pcr->card_drive_sel |= rtsx_reg_to_card_drive_sel(reg); - rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG2, ®); + pci_read_config_dword(pdev, PCR_SETTING_REG2, ®); pcr_dbg(pcr, "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG2, reg); pcr->sd30_drive_sel_3v3 = rtsx_reg_to_sd30_drive_sel_3v3(reg); if (rtsx_reg_check_reverse_socket(reg)) @@ -93,14 +94,15 @@ static void rtsx_base_force_power_down(struct rtsx_pcr *pcr, u8 pm_state) static void rts5249_init_from_cfg(struct rtsx_pcr *pcr) { + struct pci_dev *pdev = pcr->pci; struct rtsx_cr_option *option = &(pcr->option); u32 lval; if (CHK_PCI_PID(pcr, PID_524A)) - rtsx_pci_read_config_dword(pcr, + pci_read_config_dword(pdev, PCR_ASPM_SETTING_REG1, &lval); else - rtsx_pci_read_config_dword(pcr, + pci_read_config_dword(pdev, PCR_ASPM_SETTING_REG2, &lval); if (lval & ASPM_L1_1_EN_MASK) @@ -118,7 +120,7 @@ static void rts5249_init_from_cfg(struct rtsx_pcr *pcr) if (option->ltr_en) { u16 val; - pcie_capability_read_word(pcr->pci, PCI_EXP_DEVCTL2, &val); + pcie_capability_read_word(pdev, PCI_EXP_DEVCTL2, &val); if (val & PCI_EXP_DEVCTL2_LTR_EN) { option->ltr_enabled = true; option->ltr_active = true; diff --git a/drivers/misc/cardreader/rts5260.c b/drivers/misc/cardreader/rts5260.c index 7a9dbb778e84..0e806dd7ad08 100644 --- a/drivers/misc/cardreader/rts5260.c +++ b/drivers/misc/cardreader/rts5260.c @@ -64,9 +64,10 @@ static void rts5260_fill_driving(struct rtsx_pcr *pcr, u8 voltage) static void rtsx_base_fetch_vendor_settings(struct rtsx_pcr *pcr) { + struct pci_dev *pdev = pcr->pci; u32 reg; - rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG1, ®); + pci_read_config_dword(pdev, PCR_SETTING_REG1, ®); pcr_dbg(pcr, "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG1, reg); if (!rtsx_vendor_setting_valid(reg)) { @@ -79,7 +80,7 @@ static void rtsx_base_fetch_vendor_settings(struct rtsx_pcr *pcr) pcr->card_drive_sel &= 0x3F; pcr->card_drive_sel |= rtsx_reg_to_card_drive_sel(reg); - rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG2, ®); + pci_read_config_dword(pdev, PCR_SETTING_REG2, ®); pcr_dbg(pcr, "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG2, reg); pcr->sd30_drive_sel_3v3 = rtsx_reg_to_sd30_drive_sel_3v3(reg); if (rtsx_reg_check_reverse_socket(reg)) @@ -496,10 +497,11 @@ static void rts5260_pwr_saving_setting(struct rtsx_pcr *pcr) static void rts5260_init_from_cfg(struct rtsx_pcr *pcr) { + struct pci_dev *pdev = pcr->pci; struct rtsx_cr_option *option = &pcr->option; u32 lval; - rtsx_pci_read_config_dword(pcr, PCR_ASPM_SETTING_5260, &lval); + pci_read_config_dword(pdev, PCR_ASPM_SETTING_5260, &lval); if (lval & ASPM_L1_1_EN_MASK) rtsx_set_dev_flag(pcr, ASPM_L1_1_EN); @@ -518,7 +520,7 @@ static void rts5260_init_from_cfg(struct rtsx_pcr *pcr) if (option->ltr_en) { u16 val; - pcie_capability_read_word(pcr->pci, PCI_EXP_DEVCTL2, &val); + pcie_capability_read_word(pdev, PCI_EXP_DEVCTL2, &val); if (val & PCI_EXP_DEVCTL2_LTR_EN) { option->ltr_enabled = true; option->ltr_active = true; diff --git a/drivers/misc/cardreader/rts5261.c b/drivers/misc/cardreader/rts5261.c index 195822ec858e..4f30637ee4ac 100644 --- a/drivers/misc/cardreader/rts5261.c +++ b/drivers/misc/cardreader/rts5261.c @@ -59,9 +59,11 @@ static void rts5261_fill_driving(struct rtsx_pcr *pcr, u8 voltage) static void rtsx5261_fetch_vendor_settings(struct rtsx_pcr *pcr) { + struct pci_dev *pdev = pcr->pci; u32 reg; + /* 0x814~0x817 */ - rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG2, ®); + pci_read_config_dword(pdev, PCR_SETTING_REG2, ®); pcr_dbg(pcr, "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG2, reg); if (!rts5261_vendor_setting_valid(reg)) { @@ -76,7 +78,7 @@ static void rtsx5261_fetch_vendor_settings(struct rtsx_pcr *pcr) pcr->flags |= PCR_REVERSE_SOCKET; /* 0x724~0x727 */ - rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG1, ®); + pci_read_config_dword(pdev, PCR_SETTING_REG1, ®); pcr_dbg(pcr, "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG1, reg); pcr->aspm_en = rts5261_reg_to_aspm(reg); @@ -361,6 +363,7 @@ static void rts5261_process_ocp(struct rtsx_pcr *pcr) static int rts5261_init_from_hw(struct rtsx_pcr *pcr) { + struct pci_dev *pdev = pcr->pci; int retval; u32 lval, i; u8 valid, efuse_valid, tmp; @@ -386,8 +389,7 @@ static int rts5261_init_from_hw(struct rtsx_pcr *pcr) pcr_dbg(pcr, "Load efuse valid: 0x%x\n", efuse_valid); if (efuse_valid == 0) { - retval = rtsx_pci_read_config_dword(pcr, - PCR_SETTING_REG2, &lval); + retval = pci_read_config_dword(pdev, PCR_SETTING_REG2, &lval); if (retval != 0) pcr_dbg(pcr, "read 0x814 DW fail\n"); pcr_dbg(pcr, "DW from 0x814: 0x%x\n", lval); @@ -399,9 +401,9 @@ static int rts5261_init_from_hw(struct rtsx_pcr *pcr) REG_EFUSE_POR, 0); pcr_dbg(pcr, "Disable efuse por!\n"); - rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG2, &lval); + pci_read_config_dword(pdev, PCR_SETTING_REG2, &lval); lval = lval & 0x00FFFFFF; - retval = rtsx_pci_write_config_dword(pcr, PCR_SETTING_REG2, lval); + retval = pci_write_config_dword(pdev, PCR_SETTING_REG2, lval); if (retval != 0) pcr_dbg(pcr, "write config fail\n"); @@ -410,10 +412,11 @@ static int rts5261_init_from_hw(struct rtsx_pcr *pcr) static void rts5261_init_from_cfg(struct rtsx_pcr *pcr) { + struct pci_dev *pdev = pcr->pci; u32 lval; struct rtsx_cr_option *option = &pcr->option; - rtsx_pci_read_config_dword(pcr, PCR_ASPM_SETTING_REG1, &lval); + pci_read_config_dword(pdev, PCR_ASPM_SETTING_REG1, &lval); if (lval & ASPM_L1_1_EN_MASK) rtsx_set_dev_flag(pcr, ASPM_L1_1_EN); @@ -439,7 +442,7 @@ static void rts5261_init_from_cfg(struct rtsx_pcr *pcr) if (option->ltr_en) { u16 val; - pcie_capability_read_word(pcr->pci, PCI_EXP_DEVCTL2, &val); + pcie_capability_read_word(pdev, PCI_EXP_DEVCTL2, &val); if (val & PCI_EXP_DEVCTL2_LTR_EN) { option->ltr_enabled = true; option->ltr_active = true; diff --git a/drivers/misc/cardreader/rtsx_pcr.c b/drivers/misc/cardreader/rtsx_pcr.c index 0c0f1dd6f00f..2fc6b938e999 100644 --- a/drivers/misc/cardreader/rtsx_pcr.c +++ b/drivers/misc/cardreader/rtsx_pcr.c @@ -1350,7 +1350,7 @@ static int rtsx_pci_init_hw(struct rtsx_pcr *pcr) pcie_capability_write_word(pdev, PCI_EXP_LNKCTL, PCI_EXP_LNKCTL_CLKREQ_EN); /* Enter L1 when host tx idle */ - rtsx_pci_write_config_byte(pcr, 0x70F, 0x5B); + pci_write_config_byte(pdev, 0x70F, 0x5B); if (pcr->ops->extra_init_hw) { err = pcr->ops->extra_init_hw(pcr); diff --git a/include/linux/rtsx_pci.h b/include/linux/rtsx_pci.h index 4ff7b221f36e..b93573c3c5fc 100644 --- a/include/linux/rtsx_pci.h +++ b/include/linux/rtsx_pci.h @@ -99,18 +99,6 @@ #define rtsx_pci_readb(pcr, reg) \ ioread8((pcr)->remap_addr + reg) -#define rtsx_pci_read_config_byte(pcr, where, val) \ - pci_read_config_byte((pcr)->pci, where, val) - -#define rtsx_pci_write_config_byte(pcr, where, val) \ - pci_write_config_byte((pcr)->pci, where, val) - -#define rtsx_pci_read_config_dword(pcr, where, val) \ - pci_read_config_dword((pcr)->pci, where, val) - -#define rtsx_pci_write_config_dword(pcr, where, val) \ - pci_write_config_dword((pcr)->pci, where, val) - #define STATE_TRANS_NONE 0 #define STATE_TRANS_CMD 1 #define STATE_TRANS_BUF 2 -- cgit v1.2.3-59-g8ed1b From ed86a9877d05b99088a409a7603828b818a433dc Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 21 Jul 2020 16:23:35 -0500 Subject: misc: rtsx: Find L1 PM Substates capability instead of hard-coding Instead of hard-coding the location of the L1 PM Substates capability based on the Device ID, search for it in the extended capabilities list. This works for any device, as long as it implements the L1 PM Substates capability correctly, so it doesn't require maintenance as new devices are added. No functional change intended. Signed-off-by: Bjorn Helgaas Acked-by: Arnd Bergmann Link: https://lore.kernel.org/r/20200721212336.1159079-5-helgaas@kernel.org [ minor addition due to differences in my tree - gregkh] Signed-off-by: Greg Kroah-Hartman --- drivers/misc/cardreader/rts5228.c | 7 ++++++- drivers/misc/cardreader/rts5249.c | 12 ++++++------ drivers/misc/cardreader/rts5260.c | 7 ++++++- drivers/misc/cardreader/rts5261.c | 7 ++++++- include/linux/rtsx_pci.h | 4 ---- 5 files changed, 24 insertions(+), 13 deletions(-) diff --git a/drivers/misc/cardreader/rts5228.c b/drivers/misc/cardreader/rts5228.c index 448929829de4..f5716c631104 100644 --- a/drivers/misc/cardreader/rts5228.c +++ b/drivers/misc/cardreader/rts5228.c @@ -379,11 +379,16 @@ static void rts5228_process_ocp(struct rtsx_pcr *pcr) static void rts5228_init_from_cfg(struct rtsx_pcr *pcr) { + struct pci_dev *pdev = pcr->pci; + int l1ss; u32 lval; struct rtsx_cr_option *option = &pcr->option; - pci_read_config_dword(pcr->pci, PCR_ASPM_SETTING_REG1, &lval); + l1ss = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_L1SS); + if (!l1ss) + return; + pci_read_config_dword(pdev, l1ss + PCI_L1SS_CTL1, &lval); if (0 == (lval & 0x0F)) rtsx_pci_enable_oobs_polling(pcr); diff --git a/drivers/misc/cardreader/rts5249.c b/drivers/misc/cardreader/rts5249.c index 665472d05993..1b8149e806c1 100644 --- a/drivers/misc/cardreader/rts5249.c +++ b/drivers/misc/cardreader/rts5249.c @@ -95,15 +95,15 @@ static void rtsx_base_force_power_down(struct rtsx_pcr *pcr, u8 pm_state) static void rts5249_init_from_cfg(struct rtsx_pcr *pcr) { struct pci_dev *pdev = pcr->pci; + int l1ss; struct rtsx_cr_option *option = &(pcr->option); u32 lval; - if (CHK_PCI_PID(pcr, PID_524A)) - pci_read_config_dword(pdev, - PCR_ASPM_SETTING_REG1, &lval); - else - pci_read_config_dword(pdev, - PCR_ASPM_SETTING_REG2, &lval); + l1ss = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_L1SS); + if (!l1ss) + return; + + pci_read_config_dword(pdev, l1ss + PCI_L1SS_CTL1, &lval); if (lval & ASPM_L1_1_EN_MASK) rtsx_set_dev_flag(pcr, ASPM_L1_1_EN); diff --git a/drivers/misc/cardreader/rts5260.c b/drivers/misc/cardreader/rts5260.c index 0e806dd7ad08..ebf77643cc90 100644 --- a/drivers/misc/cardreader/rts5260.c +++ b/drivers/misc/cardreader/rts5260.c @@ -498,10 +498,15 @@ static void rts5260_pwr_saving_setting(struct rtsx_pcr *pcr) static void rts5260_init_from_cfg(struct rtsx_pcr *pcr) { struct pci_dev *pdev = pcr->pci; + int l1ss; struct rtsx_cr_option *option = &pcr->option; u32 lval; - pci_read_config_dword(pdev, PCR_ASPM_SETTING_5260, &lval); + l1ss = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_L1SS); + if (!l1ss) + return; + + pci_read_config_dword(pdev, l1ss + PCI_L1SS_CTL1, &lval); if (lval & ASPM_L1_1_EN_MASK) rtsx_set_dev_flag(pcr, ASPM_L1_1_EN); diff --git a/drivers/misc/cardreader/rts5261.c b/drivers/misc/cardreader/rts5261.c index 4f30637ee4ac..4b6e3fe4a007 100644 --- a/drivers/misc/cardreader/rts5261.c +++ b/drivers/misc/cardreader/rts5261.c @@ -413,10 +413,15 @@ static int rts5261_init_from_hw(struct rtsx_pcr *pcr) static void rts5261_init_from_cfg(struct rtsx_pcr *pcr) { struct pci_dev *pdev = pcr->pci; + int l1ss; u32 lval; struct rtsx_cr_option *option = &pcr->option; - pci_read_config_dword(pdev, PCR_ASPM_SETTING_REG1, &lval); + l1ss = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_L1SS); + if (!l1ss) + return; + + pci_read_config_dword(pdev, l1ss + PCI_L1SS_CTL1, &lval); if (lval & ASPM_L1_1_EN_MASK) rtsx_set_dev_flag(pcr, ASPM_L1_1_EN); diff --git a/include/linux/rtsx_pci.h b/include/linux/rtsx_pci.h index b93573c3c5fc..f146ca413f38 100644 --- a/include/linux/rtsx_pci.h +++ b/include/linux/rtsx_pci.h @@ -1037,10 +1037,6 @@ #define PHY_DIG1E_RX_EN_KEEP 0x0001 #define PHY_DUM_REG 0x1F -#define PCR_ASPM_SETTING_REG1 0x160 -#define PCR_ASPM_SETTING_REG2 0x168 -#define PCR_ASPM_SETTING_5260 0x178 - #define PCR_SETTING_REG1 0x724 #define PCR_SETTING_REG2 0x814 #define PCR_SETTING_REG3 0x747 -- cgit v1.2.3-59-g8ed1b From 7a4462a96777b64b22412f782de226c90290bf75 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 21 Jul 2020 16:23:36 -0500 Subject: misc: rtsx: Use standard PCI definitions When reading registers defined by the PCIe spec, use the names already defined by the PCI core. This makes maintenance of the PCI core and drivers easier. No functional change intended. Signed-off-by: Bjorn Helgaas Acked-by: Arnd Bergmann Link: https://lore.kernel.org/r/20200721212336.1159079-6-helgaas@kernel.org [ additional replacements due to changes in my tree - gregkh ] Signed-off-by: Greg Kroah-Hartman --- drivers/misc/cardreader/rts5228.c | 8 ++++---- drivers/misc/cardreader/rts5249.c | 8 ++++---- drivers/misc/cardreader/rts5260.c | 8 ++++---- drivers/misc/cardreader/rts5261.c | 8 ++++---- include/linux/rtsx_pci.h | 5 ----- 5 files changed, 16 insertions(+), 21 deletions(-) diff --git a/drivers/misc/cardreader/rts5228.c b/drivers/misc/cardreader/rts5228.c index f5716c631104..28feab1449ab 100644 --- a/drivers/misc/cardreader/rts5228.c +++ b/drivers/misc/cardreader/rts5228.c @@ -395,22 +395,22 @@ static void rts5228_init_from_cfg(struct rtsx_pcr *pcr) else rtsx_pci_disable_oobs_polling(pcr); - if (lval & ASPM_L1_1_EN_MASK) + if (lval & PCI_L1SS_CTL1_ASPM_L1_1) rtsx_set_dev_flag(pcr, ASPM_L1_1_EN); else rtsx_clear_dev_flag(pcr, ASPM_L1_1_EN); - if (lval & ASPM_L1_2_EN_MASK) + if (lval & PCI_L1SS_CTL1_ASPM_L1_2) rtsx_set_dev_flag(pcr, ASPM_L1_2_EN); else rtsx_clear_dev_flag(pcr, ASPM_L1_2_EN); - if (lval & PM_L1_1_EN_MASK) + if (lval & PCI_L1SS_CTL1_PCIPM_L1_1) rtsx_set_dev_flag(pcr, PM_L1_1_EN); else rtsx_clear_dev_flag(pcr, PM_L1_1_EN); - if (lval & PM_L1_2_EN_MASK) + if (lval & PCI_L1SS_CTL1_PCIPM_L1_2) rtsx_set_dev_flag(pcr, PM_L1_2_EN); else rtsx_clear_dev_flag(pcr, PM_L1_2_EN); diff --git a/drivers/misc/cardreader/rts5249.c b/drivers/misc/cardreader/rts5249.c index 1b8149e806c1..941b3d77f1e9 100644 --- a/drivers/misc/cardreader/rts5249.c +++ b/drivers/misc/cardreader/rts5249.c @@ -105,16 +105,16 @@ static void rts5249_init_from_cfg(struct rtsx_pcr *pcr) pci_read_config_dword(pdev, l1ss + PCI_L1SS_CTL1, &lval); - if (lval & ASPM_L1_1_EN_MASK) + if (lval & PCI_L1SS_CTL1_ASPM_L1_1) rtsx_set_dev_flag(pcr, ASPM_L1_1_EN); - if (lval & ASPM_L1_2_EN_MASK) + if (lval & PCI_L1SS_CTL1_ASPM_L1_2) rtsx_set_dev_flag(pcr, ASPM_L1_2_EN); - if (lval & PM_L1_1_EN_MASK) + if (lval & PCI_L1SS_CTL1_PCIPM_L1_1) rtsx_set_dev_flag(pcr, PM_L1_1_EN); - if (lval & PM_L1_2_EN_MASK) + if (lval & PCI_L1SS_CTL1_PCIPM_L1_2) rtsx_set_dev_flag(pcr, PM_L1_2_EN); if (option->ltr_en) { diff --git a/drivers/misc/cardreader/rts5260.c b/drivers/misc/cardreader/rts5260.c index ebf77643cc90..b9f66b1384a6 100644 --- a/drivers/misc/cardreader/rts5260.c +++ b/drivers/misc/cardreader/rts5260.c @@ -508,16 +508,16 @@ static void rts5260_init_from_cfg(struct rtsx_pcr *pcr) pci_read_config_dword(pdev, l1ss + PCI_L1SS_CTL1, &lval); - if (lval & ASPM_L1_1_EN_MASK) + if (lval & PCI_L1SS_CTL1_ASPM_L1_1) rtsx_set_dev_flag(pcr, ASPM_L1_1_EN); - if (lval & ASPM_L1_2_EN_MASK) + if (lval & PCI_L1SS_CTL1_ASPM_L1_2) rtsx_set_dev_flag(pcr, ASPM_L1_2_EN); - if (lval & PM_L1_1_EN_MASK) + if (lval & PCI_L1SS_CTL1_PCIPM_L1_1) rtsx_set_dev_flag(pcr, PM_L1_1_EN); - if (lval & PM_L1_2_EN_MASK) + if (lval & PCI_L1SS_CTL1_PCIPM_L1_2) rtsx_set_dev_flag(pcr, PM_L1_2_EN); rts5260_pwr_saving_setting(pcr); diff --git a/drivers/misc/cardreader/rts5261.c b/drivers/misc/cardreader/rts5261.c index 4b6e3fe4a007..471961487ff8 100644 --- a/drivers/misc/cardreader/rts5261.c +++ b/drivers/misc/cardreader/rts5261.c @@ -423,22 +423,22 @@ static void rts5261_init_from_cfg(struct rtsx_pcr *pcr) pci_read_config_dword(pdev, l1ss + PCI_L1SS_CTL1, &lval); - if (lval & ASPM_L1_1_EN_MASK) + if (lval & PCI_L1SS_CTL1_ASPM_L1_1) rtsx_set_dev_flag(pcr, ASPM_L1_1_EN); else rtsx_clear_dev_flag(pcr, ASPM_L1_1_EN); - if (lval & ASPM_L1_2_EN_MASK) + if (lval & PCI_L1SS_CTL1_ASPM_L1_2) rtsx_set_dev_flag(pcr, ASPM_L1_2_EN); else rtsx_clear_dev_flag(pcr, ASPM_L1_2_EN); - if (lval & PM_L1_1_EN_MASK) + if (lval & PCI_L1SS_CTL1_PCIPM_L1_1) rtsx_set_dev_flag(pcr, PM_L1_1_EN); else rtsx_clear_dev_flag(pcr, PM_L1_1_EN); - if (lval & PM_L1_2_EN_MASK) + if (lval & PCI_L1SS_CTL1_PCIPM_L1_2) rtsx_set_dev_flag(pcr, PM_L1_2_EN); else rtsx_clear_dev_flag(pcr, PM_L1_2_EN); diff --git a/include/linux/rtsx_pci.h b/include/linux/rtsx_pci.h index f146ca413f38..745f5e73f99a 100644 --- a/include/linux/rtsx_pci.h +++ b/include/linux/rtsx_pci.h @@ -1083,11 +1083,6 @@ struct pcr_ops { enum PDEV_STAT {PDEV_STAT_IDLE, PDEV_STAT_RUN}; -#define ASPM_L1_1_EN_MASK BIT(3) -#define ASPM_L1_2_EN_MASK BIT(2) -#define PM_L1_1_EN_MASK BIT(1) -#define PM_L1_2_EN_MASK BIT(0) - #define ASPM_L1_1_EN BIT(0) #define ASPM_L1_2_EN BIT(1) #define PM_L1_1_EN BIT(2) -- cgit v1.2.3-59-g8ed1b From c56967d674e361ebe716e66992e3c5332b25ac1f Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Wed, 22 Jul 2020 13:15:34 -0500 Subject: mei: hdcp: Replace one-element 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]. Also, make use of the array_size() helper instead of the open-coded version in memcpy(). These sorts of multiplication factors need to be wrapped in array_size(). And while there, use the preferred form for passing a size of a struct. The alternative form where struct name is spelled out hurts readability and introduces an opportunity for a bug when the pointer variable type is changed but the corresponding sizeof that is passed as argument is not. [1] https://en.wikipedia.org/wiki/Flexible_array_member [2] https://github.com/KSPP/linux/issues/79 Signed-off-by: Gustavo A. R. Silva Link: https://lore.kernel.org/r/20200722181534.GA31357@embeddedor Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/hdcp/mei_hdcp.c | 2 +- drivers/misc/mei/hdcp/mei_hdcp.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/misc/mei/hdcp/mei_hdcp.c b/drivers/misc/mei/hdcp/mei_hdcp.c index e6c3dc595617..d1d3e025ca0e 100644 --- a/drivers/misc/mei/hdcp/mei_hdcp.c +++ b/drivers/misc/mei/hdcp/mei_hdcp.c @@ -572,7 +572,7 @@ static int mei_hdcp_verify_mprime(struct device *dev, HDCP_2_2_MPRIME_LEN); drm_hdcp_cpu_to_be24(verify_mprime_in.seq_num_m, data->seq_num_m); memcpy(verify_mprime_in.streams, data->streams, - (data->k * sizeof(struct hdcp2_streamid_type))); + array_size(data->k, sizeof(*data->streams))); verify_mprime_in.k = cpu_to_be16(data->k); diff --git a/drivers/misc/mei/hdcp/mei_hdcp.h b/drivers/misc/mei/hdcp/mei_hdcp.h index 18ffc773fa18..834757f5e072 100644 --- a/drivers/misc/mei/hdcp/mei_hdcp.h +++ b/drivers/misc/mei/hdcp/mei_hdcp.h @@ -358,7 +358,7 @@ struct wired_cmd_repeater_auth_stream_req_in { u8 seq_num_m[HDCP_2_2_SEQ_NUM_LEN]; u8 m_prime[HDCP_2_2_MPRIME_LEN]; __be16 k; - struct hdcp2_streamid_type streams[1]; + struct hdcp2_streamid_type streams[]; } __packed; struct wired_cmd_repeater_auth_stream_req_out { -- cgit v1.2.3-59-g8ed1b From 74b04fae4501f2b0424b01f0295bf26c5c8fce5a Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Fri, 17 Jul 2020 20:59:25 +0200 Subject: mei: Replace HTTP links with HTTPS ones Rationale: Reduces attack surface on kernel devs opening the links for MITM as HTTPS traffic is much harder to manipulate. Deterministic algorithm: For each file: If not .svg: For each line: If doesn't contain `\bxmlns\b`: For each link, `\bhttp://[^# \t\r\n]*(?:\w|/)`: If neither `\bgnu\.org/license`, nor `\bmozilla\.org/MPL\b`: If both the HTTP and HTTPS versions return 200 OK and serve the same content: Replace HTTP with HTTPS. Signed-off-by: Alexander A. Klimov Acked-by: Tomas Winkler Link: https://lore.kernel.org/r/20200717185925.84102-1-grandmaster@al2klimov.de Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/mei/Kconfig b/drivers/misc/mei/Kconfig index 9d7b3719bfa0..f5fd5b786607 100644 --- a/drivers/misc/mei/Kconfig +++ b/drivers/misc/mei/Kconfig @@ -9,7 +9,7 @@ config INTEL_MEI if selected /dev/mei misc device will be created. For more information see - + config INTEL_MEI_ME tristate "ME Enabled Intel Chipsets" -- cgit v1.2.3-59-g8ed1b From 3c3b7ddef7879abb2c42422e898145826c79e5f0 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Tue, 14 Jul 2020 16:45:16 -0500 Subject: mei: Avoid the use of one-element arrays One-element arrays are being deprecated[1]. Replace the one-element arrays with a simple value type u8 reserved, once this is just a placeholder for alignment. Also, while there, use the preferred form for passing a size of a struct. The alternative form where struct name is spelled out hurts readability and introduces an opportunity for a bug when the variable type is changed but the corresponding sizeof that is passed as argument is not. [1] https://github.com/KSPP/linux/issues/79 Signed-off-by: Gustavo A. R. Silva Link: https://lore.kernel.org/r/20200714214516.GA1040@embeddedor Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/hbm.c | 4 ++-- drivers/misc/mei/hw.h | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c index a44094cdbc36..f020d5594154 100644 --- a/drivers/misc/mei/hbm.c +++ b/drivers/misc/mei/hbm.c @@ -408,14 +408,14 @@ static int mei_hbm_add_cl_resp(struct mei_device *dev, u8 addr, u8 status) { struct mei_msg_hdr mei_hdr; struct hbm_add_client_response resp; - const size_t len = sizeof(struct hbm_add_client_response); + const size_t len = sizeof(resp); int ret; dev_dbg(dev->dev, "adding client response\n"); mei_hbm_hdr(&mei_hdr, len); - memset(&resp, 0, sizeof(struct hbm_add_client_response)); + memset(&resp, 0, len); resp.hbm_cmd = MEI_HBM_ADD_CLIENT_RES_CMD; resp.me_addr = addr; resp.status = status; diff --git a/drivers/misc/mei/hw.h b/drivers/misc/mei/hw.h index b1a8d5ec88b3..8c0297f0e7f3 100644 --- a/drivers/misc/mei/hw.h +++ b/drivers/misc/mei/hw.h @@ -346,13 +346,13 @@ struct hbm_add_client_request { * @hbm_cmd: bus message command header * @me_addr: address of the client in ME * @status: if HBMS_SUCCESS then the client can now accept connections. - * @reserved: reserved + * @reserved: reserved for alignment. */ struct hbm_add_client_response { u8 hbm_cmd; u8 me_addr; u8 status; - u8 reserved[1]; + u8 reserved; } __packed; /** @@ -461,7 +461,7 @@ struct hbm_notification { u8 hbm_cmd; u8 me_addr; u8 host_addr; - u8 reserved[1]; + u8 reserved; } __packed; /** -- cgit v1.2.3-59-g8ed1b From 34ec0aa62b40e55bcd5f41b89494242c38556f30 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 18 Jul 2020 17:29:43 -0700 Subject: misc: mic: : drop a duplicated word Drop the repeated word "the" in a comment. Signed-off-by: Randy Dunlap Cc: Sudeep Dutt Cc: Ashutosh Dixit Cc: Arnd Bergmann Cc: Greg Kroah-Hartman Link: https://lore.kernel.org/r/20200719002943.20624-1-rdunlap@infradead.org Signed-off-by: Greg Kroah-Hartman --- include/linux/mic_bus.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/mic_bus.h b/include/linux/mic_bus.h index 491156a2359f..e99c789424e0 100644 --- a/include/linux/mic_bus.h +++ b/include/linux/mic_bus.h @@ -6,7 +6,7 @@ * * Intel MIC Bus driver. * - * This implementation is very similar to the the virtio bus driver + * This implementation is very similar to the virtio bus driver * implementation @ include/linux/virtio.h. */ #ifndef _MIC_BUS_H_ -- cgit v1.2.3-59-g8ed1b From 1859f4ebcf2dfe2e878a3805700036fdc4e01e2c Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 18 Jul 2020 17:27:38 -0700 Subject: android: binder.h: drop a duplicated word MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drop the repeated word "the" in a comment. Cc: Arve Hjønnevåg Cc: Todd Kjos Cc: Martijn Coenen Cc: Joel Fernandes Cc: Hridya Valsaraju Cc: Suren Baghdasaryan Cc: devel@driverdev.osuosl.org Acked-by: Christian Brauner Signed-off-by: Randy Dunlap Link: https://lore.kernel.org/r/20200719002738.20210-1-rdunlap@infradead.org Signed-off-by: Greg Kroah-Hartman --- include/uapi/linux/android/binder.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/uapi/linux/android/binder.h b/include/uapi/linux/android/binder.h index 2832134e5397..f1ce2c4c077e 100644 --- a/include/uapi/linux/android/binder.h +++ b/include/uapi/linux/android/binder.h @@ -404,7 +404,7 @@ enum binder_driver_return_protocol { BR_FAILED_REPLY = _IO('r', 17), /* - * The the last transaction (either a bcTRANSACTION or + * The last transaction (either a bcTRANSACTION or * a bcATTEMPT_ACQUIRE) failed (e.g. out of memory). No parameters. */ }; -- cgit v1.2.3-59-g8ed1b From a52c809769d3b4d5c94c96e9b49d898ce17128f0 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 13 Jul 2020 18:40:24 +0200 Subject: misc: Replace HTTP links with HTTPS ones Rationale: Reduces attack surface on kernel devs opening the links for MITM as HTTPS traffic is much harder to manipulate. Deterministic algorithm: For each file: If not .svg: For each line: If doesn't contain `\bxmlns\b`: For each link, `\bhttp://[^# \t\r\n]*(?:\w|/)`: If neither `\bgnu\.org/license`, nor `\bmozilla\.org/MPL\b`: If both the HTTP and HTTPS versions return 200 OK and serve the same content: Replace HTTP with HTTPS. Signed-off-by: Alexander A. Klimov Link: https://lore.kernel.org/r/20200713164024.35988-1-grandmaster@al2klimov.de Signed-off-by: Greg Kroah-Hartman --- drivers/misc/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index e1b1ba5e2b92..1a11e1ca9ab4 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -83,7 +83,7 @@ config IBM_ASM WARNING: This software may not be supported or function correctly on your IBM server. Please consult the IBM ServerProven - website + website for information on the specific driver level and support statement for your IBM server. -- cgit v1.2.3-59-g8ed1b From 4e74eeb27e2873e923fee883df64341dae9ecdbb Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 13 Jul 2020 12:44:53 +0200 Subject: char: Replace HTTP links with HTTPS ones Rationale: Reduces attack surface on kernel devs opening the links for MITM as HTTPS traffic is much harder to manipulate. Deterministic algorithm: For each file: If not .svg: For each line: If doesn't contain `\bxmlns\b`: For each link, `\bhttp://[^# \t\r\n]*(?:\w|/)`: If neither `\bgnu\.org/license`, nor `\bmozilla\.org/MPL\b`: If both the HTTP and HTTPS versions return 200 OK and serve the same content: Replace HTTP with HTTPS. Signed-off-by: Alexander A. Klimov Link: https://lore.kernel.org/r/20200713104453.33414-1-grandmaster@al2klimov.de Signed-off-by: Greg Kroah-Hartman --- drivers/char/Kconfig | 6 +++--- drivers/misc/echo/echo.c | 6 +++--- drivers/misc/sram-exec.c | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 98c3a5d8003e..b1bd336761b1 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -38,7 +38,7 @@ config PRINTER box (as opposed to using a serial printer; if the connector at the printer has 9 or 25 holes ["female"], then it's serial), say Y. Also read the Printing-HOWTO, available from - . + . It is possible to share one parallel port among several devices (e.g. printer and ZIP drive) and it is safe to compile the @@ -201,7 +201,7 @@ config DTLK depends on ISA help This driver is for the DoubleTalk PC, a speech synthesizer - manufactured by RC Systems (). It is also + manufactured by RC Systems (). It is also called the `internal DoubleTalk'. To compile this driver as a module, choose M here: the @@ -237,7 +237,7 @@ config APPLICOM This driver provides the kernel-side support for the intelligent fieldbus cards made by Applicom International. More information about these cards can be found on the WWW at the address - , or by email from David Woodhouse + , or by email from David Woodhouse . To compile this driver as a module, choose M here: the diff --git a/drivers/misc/echo/echo.c b/drivers/misc/echo/echo.c index 713e92ee27ac..3c4eaba86576 100644 --- a/drivers/misc/echo/echo.c +++ b/drivers/misc/echo/echo.c @@ -66,13 +66,13 @@ Path Models", IEEE Transactions on communications, COM-25, No. 6, June 1977. - http://www.rowetel.com/images/echo/dual_path_paper.pdf + https://www.rowetel.com/images/echo/dual_path_paper.pdf [2] The classic, very useful paper that tells you how to actually build a real world echo canceller: Messerschmitt, Hedberg, Cole, Haoui, Winship, "Digital Voice Echo Canceller with a TMS320020, - http://www.rowetel.com/images/echo/spra129.pdf + https://www.rowetel.com/images/echo/spra129.pdf [3] I have written a series of blog posts on this work, here is Part 1: http://www.rowetel.com/blog/?p=18 @@ -80,7 +80,7 @@ [4] The source code http://svn.rowetel.com/software/oslec/ [5] A nice reference on LMS filters: - http://en.wikipedia.org/wiki/Least_mean_squares_filter + https://en.wikipedia.org/wiki/Least_mean_squares_filter Credits: diff --git a/drivers/misc/sram-exec.c b/drivers/misc/sram-exec.c index cb57ac6ab4c3..6cc31789b38d 100644 --- a/drivers/misc/sram-exec.c +++ b/drivers/misc/sram-exec.c @@ -1,7 +1,7 @@ /* * SRAM protect-exec region helper functions * - * Copyright (C) 2017 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2017 Texas Instruments Incorporated - https://www.ti.com/ * Dave Gerlach * * This program is free software; you can redistribute it and/or modify -- cgit v1.2.3-59-g8ed1b From 146f90afed258b409104aa6ecc421b823ea1b406 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sat, 18 Jul 2020 09:02:24 +0200 Subject: misc: hpilo: switch from 'pci_' to 'dma_' API The wrappers in include/linux/pci-dma-compat.h should go away. The patch has been generated with the coccinelle script below and has been hand modified to replace GFP_ with a correct flag. It has been compile tested. When memory is allocated in 'ilo_ccb_setup()' GFP_ATOMIC must be used because a spin_lock is hold in 'ilo_open()' before calling 'ilo_ccb_setup()' @@ @@ - PCI_DMA_BIDIRECTIONAL + DMA_BIDIRECTIONAL @@ @@ - PCI_DMA_TODEVICE + DMA_TO_DEVICE @@ @@ - PCI_DMA_FROMDEVICE + DMA_FROM_DEVICE @@ @@ - PCI_DMA_NONE + DMA_NONE @@ expression e1, e2, e3; @@ - pci_alloc_consistent(e1, e2, e3) + dma_alloc_coherent(&e1->dev, e2, e3, GFP_) @@ expression e1, e2, e3; @@ - pci_zalloc_consistent(e1, e2, e3) + dma_alloc_coherent(&e1->dev, e2, e3, GFP_) @@ expression e1, e2, e3, e4; @@ - pci_free_consistent(e1, e2, e3, e4) + dma_free_coherent(&e1->dev, e2, e3, e4) @@ expression e1, e2, e3, e4; @@ - pci_map_single(e1, e2, e3, e4) + dma_map_single(&e1->dev, e2, e3, e4) @@ expression e1, e2, e3, e4; @@ - pci_unmap_single(e1, e2, e3, e4) + dma_unmap_single(&e1->dev, e2, e3, e4) @@ expression e1, e2, e3, e4, e5; @@ - pci_map_page(e1, e2, e3, e4, e5) + dma_map_page(&e1->dev, e2, e3, e4, e5) @@ expression e1, e2, e3, e4; @@ - pci_unmap_page(e1, e2, e3, e4) + dma_unmap_page(&e1->dev, e2, e3, e4) @@ expression e1, e2, e3, e4; @@ - pci_map_sg(e1, e2, e3, e4) + dma_map_sg(&e1->dev, e2, e3, e4) @@ expression e1, e2, e3, e4; @@ - pci_unmap_sg(e1, e2, e3, e4) + dma_unmap_sg(&e1->dev, e2, e3, e4) @@ expression e1, e2, e3, e4; @@ - pci_dma_sync_single_for_cpu(e1, e2, e3, e4) + dma_sync_single_for_cpu(&e1->dev, e2, e3, e4) @@ expression e1, e2, e3, e4; @@ - pci_dma_sync_single_for_device(e1, e2, e3, e4) + dma_sync_single_for_device(&e1->dev, e2, e3, e4) @@ expression e1, e2, e3, e4; @@ - pci_dma_sync_sg_for_cpu(e1, e2, e3, e4) + dma_sync_sg_for_cpu(&e1->dev, e2, e3, e4) @@ expression e1, e2, e3, e4; @@ - pci_dma_sync_sg_for_device(e1, e2, e3, e4) + dma_sync_sg_for_device(&e1->dev, e2, e3, e4) @@ expression e1, e2; @@ - pci_dma_mapping_error(e1, e2) + dma_mapping_error(&e1->dev, e2) @@ expression e1, e2; @@ - pci_set_dma_mask(e1, e2) + dma_set_mask(&e1->dev, e2) @@ expression e1, e2; @@ - pci_set_consistent_dma_mask(e1, e2) + dma_set_coherent_mask(&e1->dev, e2) Signed-off-by: Christophe JAILLET Link: https://lore.kernel.org/r/20200718070224.337964-1-christophe.jaillet@wanadoo.fr Signed-off-by: Greg Kroah-Hartman --- drivers/misc/hpilo.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/misc/hpilo.c b/drivers/misc/hpilo.c index 10c975662f8b..c9539c89a925 100644 --- a/drivers/misc/hpilo.c +++ b/drivers/misc/hpilo.c @@ -256,7 +256,8 @@ static void ilo_ccb_close(struct pci_dev *pdev, struct ccb_data *data) memset_io(device_ccb, 0, sizeof(struct ccb)); /* free resources used to back send/recv queues */ - pci_free_consistent(pdev, data->dma_size, data->dma_va, data->dma_pa); + dma_free_coherent(&pdev->dev, data->dma_size, data->dma_va, + data->dma_pa); } static int ilo_ccb_setup(struct ilo_hwinfo *hw, struct ccb_data *data, int slot) @@ -272,8 +273,8 @@ static int ilo_ccb_setup(struct ilo_hwinfo *hw, struct ccb_data *data, int slot) 2 * desc_mem_sz(NR_QENTRY) + ILO_START_ALIGN + ILO_CACHE_SZ; - data->dma_va = pci_alloc_consistent(hw->ilo_dev, data->dma_size, - &data->dma_pa); + data->dma_va = dma_alloc_coherent(&hw->ilo_dev->dev, data->dma_size, + &data->dma_pa, GFP_ATOMIC); if (!data->dma_va) return -ENOMEM; -- cgit v1.2.3-59-g8ed1b From 21ee9b1995fc01510352d4a55c5316283293b020 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sat, 18 Jul 2020 09:02:46 +0200 Subject: misc: hpilo: avoid a useless memset Avoid a memset after a call to 'dma_alloc_coherent()'. This is useless since commit 518a2f1925c3 ("dma-mapping: zero memory returned from dma_alloc_*") Signed-off-by: Christophe JAILLET Link: https://lore.kernel.org/r/20200718070246.338016-1-christophe.jaillet@wanadoo.fr Signed-off-by: Greg Kroah-Hartman --- drivers/misc/hpilo.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/misc/hpilo.c b/drivers/misc/hpilo.c index c9539c89a925..fea3ae9d8686 100644 --- a/drivers/misc/hpilo.c +++ b/drivers/misc/hpilo.c @@ -281,8 +281,6 @@ static int ilo_ccb_setup(struct ilo_hwinfo *hw, struct ccb_data *data, int slot) dma_va = (char *)data->dma_va; dma_pa = data->dma_pa; - memset(dma_va, 0, data->dma_size); - dma_va = (char *)roundup((unsigned long)dma_va, ILO_START_ALIGN); dma_pa = roundup(dma_pa, ILO_START_ALIGN); -- cgit v1.2.3-59-g8ed1b From 8f27d659dfe67f4d70ba31120d7118055823f667 Mon Sep 17 00:00:00 2001 From: Vaibhav Gupta Date: Mon, 20 Jul 2020 15:47:23 +0530 Subject: cardreader/rtsx_pcr.c: use generic power management Drivers should not use legacy power management as they have to manage power states and related operations, for the device, themselves. This driver was handling them with the help of PCI helper functions like pci_save/restore_state(), pci_enable/disable_device(), etc. With generic PM, all essentials will be handled by the PCI core. Driver needs to do only device-specific operations. The driver was also using pci_enable_wake(...,..., 0) to disable wake. Use device_wakeup_disable() instead. Compile-tested only. Signed-off-by: Vaibhav Gupta Link: https://lore.kernel.org/r/20200720101722.145211-1-vaibhavgupta40@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/cardreader/rtsx_pcr.c | 33 +++++++++++---------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/drivers/misc/cardreader/rtsx_pcr.c b/drivers/misc/cardreader/rtsx_pcr.c index 2fc6b938e999..37ccc67f4914 100644 --- a/drivers/misc/cardreader/rtsx_pcr.c +++ b/drivers/misc/cardreader/rtsx_pcr.c @@ -1096,8 +1096,7 @@ static void rtsx_pci_idle_work(struct work_struct *work) mutex_unlock(&pcr->pcr_mutex); } -#ifdef CONFIG_PM -static void rtsx_pci_power_off(struct rtsx_pcr *pcr, u8 pm_state) +static void __maybe_unused rtsx_pci_power_off(struct rtsx_pcr *pcr, u8 pm_state) { if (pcr->ops->turn_off_led) pcr->ops->turn_off_led(pcr); @@ -1111,7 +1110,6 @@ static void rtsx_pci_power_off(struct rtsx_pcr *pcr, u8 pm_state) if (pcr->ops->force_power_down) pcr->ops->force_power_down(pcr, pm_state); } -#endif void rtsx_pci_enable_ocp(struct rtsx_pcr *pcr) { @@ -1632,10 +1630,9 @@ static void rtsx_pci_remove(struct pci_dev *pcidev) pci_name(pcidev), (int)pcidev->vendor, (int)pcidev->device); } -#ifdef CONFIG_PM - -static int rtsx_pci_suspend(struct pci_dev *pcidev, pm_message_t state) +static int __maybe_unused rtsx_pci_suspend(struct device *dev_d) { + struct pci_dev *pcidev = to_pci_dev(dev_d); struct pcr_handle *handle; struct rtsx_pcr *pcr; @@ -1651,17 +1648,15 @@ static int rtsx_pci_suspend(struct pci_dev *pcidev, pm_message_t state) rtsx_pci_power_off(pcr, HOST_ENTER_S3); - pci_save_state(pcidev); - pci_enable_wake(pcidev, pci_choose_state(pcidev, state), 0); - pci_disable_device(pcidev); - pci_set_power_state(pcidev, pci_choose_state(pcidev, state)); + device_wakeup_disable(dev_d); mutex_unlock(&pcr->pcr_mutex); return 0; } -static int rtsx_pci_resume(struct pci_dev *pcidev) +static int __maybe_unused rtsx_pci_resume(struct device *dev_d) { + struct pci_dev *pcidev = to_pci_dev(dev_d); struct pcr_handle *handle; struct rtsx_pcr *pcr; int ret = 0; @@ -1673,13 +1668,6 @@ static int rtsx_pci_resume(struct pci_dev *pcidev) mutex_lock(&pcr->pcr_mutex); - pci_set_power_state(pcidev, PCI_D0); - pci_restore_state(pcidev); - ret = pci_enable_device(pcidev); - if (ret) - goto out; - pci_set_master(pcidev); - ret = rtsx_pci_write_register(pcr, HOST_SLEEP_STATE, 0x03, 0x00); if (ret) goto out; @@ -1695,6 +1683,8 @@ out: return ret; } +#ifdef CONFIG_PM + static void rtsx_pci_shutdown(struct pci_dev *pcidev) { struct pcr_handle *handle; @@ -1714,19 +1704,18 @@ static void rtsx_pci_shutdown(struct pci_dev *pcidev) #else /* CONFIG_PM */ -#define rtsx_pci_suspend NULL -#define rtsx_pci_resume NULL #define rtsx_pci_shutdown NULL #endif /* CONFIG_PM */ +static SIMPLE_DEV_PM_OPS(rtsx_pci_pm_ops, rtsx_pci_suspend, rtsx_pci_resume); + static struct pci_driver rtsx_pci_driver = { .name = DRV_NAME_RTSX_PCI, .id_table = rtsx_pci_ids, .probe = rtsx_pci_probe, .remove = rtsx_pci_remove, - .suspend = rtsx_pci_suspend, - .resume = rtsx_pci_resume, + .driver.pm = &rtsx_pci_pm_ops, .shutdown = rtsx_pci_shutdown, }; module_pci_driver(rtsx_pci_driver); -- cgit v1.2.3-59-g8ed1b From a3b7a581823857fab4fae71be9d1c830af78a766 Mon Sep 17 00:00:00 2001 From: Ioana Ciornei Date: Fri, 17 Jul 2020 18:47:58 +0300 Subject: bus: fsl-mc: add missing device types The MC bus has different types of devices that can be discovered on the bus. Add the missing device types. Signed-off-by: Ioana Ciornei Reviewed-by: Laurentiu Tudor Link: https://lore.kernel.org/r/20200717154800.17169-2-ioana.ciornei@nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/bus/fsl-mc/fsl-mc-bus.c | 30 ++++++++++++++++++++++++++++++ include/linux/fsl/mc.h | 30 ++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/drivers/bus/fsl-mc/fsl-mc-bus.c b/drivers/bus/fsl-mc/fsl-mc-bus.c index 40526da5c6a6..ee1873438e97 100644 --- a/drivers/bus/fsl-mc/fsl-mc-bus.c +++ b/drivers/bus/fsl-mc/fsl-mc-bus.c @@ -201,6 +201,31 @@ struct device_type fsl_mc_bus_dpseci_type = { }; EXPORT_SYMBOL_GPL(fsl_mc_bus_dpseci_type); +struct device_type fsl_mc_bus_dpdmux_type = { + .name = "fsl_mc_bus_dpdmux" +}; +EXPORT_SYMBOL_GPL(fsl_mc_bus_dpdmux_type); + +struct device_type fsl_mc_bus_dpdcei_type = { + .name = "fsl_mc_bus_dpdcei" +}; +EXPORT_SYMBOL_GPL(fsl_mc_bus_dpdcei_type); + +struct device_type fsl_mc_bus_dpaiop_type = { + .name = "fsl_mc_bus_dpaiop" +}; +EXPORT_SYMBOL_GPL(fsl_mc_bus_dpaiop_type); + +struct device_type fsl_mc_bus_dpci_type = { + .name = "fsl_mc_bus_dpci" +}; +EXPORT_SYMBOL_GPL(fsl_mc_bus_dpci_type); + +struct device_type fsl_mc_bus_dpdmai_type = { + .name = "fsl_mc_bus_dpdmai" +}; +EXPORT_SYMBOL_GPL(fsl_mc_bus_dpdmai_type); + static struct device_type *fsl_mc_get_device_type(const char *type) { static const struct { @@ -217,6 +242,11 @@ static struct device_type *fsl_mc_get_device_type(const char *type) { &fsl_mc_bus_dpmac_type, "dpmac" }, { &fsl_mc_bus_dprtc_type, "dprtc" }, { &fsl_mc_bus_dpseci_type, "dpseci" }, + { &fsl_mc_bus_dpdmux_type, "dpdmux" }, + { &fsl_mc_bus_dpdcei_type, "dpdcei" }, + { &fsl_mc_bus_dpaiop_type, "dpaiop" }, + { &fsl_mc_bus_dpci_type, "dpci" }, + { &fsl_mc_bus_dpdmai_type, "dpdmai" }, { NULL, NULL } }; int i; diff --git a/include/linux/fsl/mc.h b/include/linux/fsl/mc.h index 2b5f8366dbe1..cdb03aca2aef 100644 --- a/include/linux/fsl/mc.h +++ b/include/linux/fsl/mc.h @@ -433,6 +433,11 @@ extern struct device_type fsl_mc_bus_dpmcp_type; extern struct device_type fsl_mc_bus_dpmac_type; extern struct device_type fsl_mc_bus_dprtc_type; extern struct device_type fsl_mc_bus_dpseci_type; +extern struct device_type fsl_mc_bus_dpdmux_type; +extern struct device_type fsl_mc_bus_dpdcei_type; +extern struct device_type fsl_mc_bus_dpaiop_type; +extern struct device_type fsl_mc_bus_dpci_type; +extern struct device_type fsl_mc_bus_dpdmai_type; static inline bool is_fsl_mc_bus_dprc(const struct fsl_mc_device *mc_dev) { @@ -454,6 +459,11 @@ static inline bool is_fsl_mc_bus_dpsw(const struct fsl_mc_device *mc_dev) return mc_dev->dev.type == &fsl_mc_bus_dpsw_type; } +static inline bool is_fsl_mc_bus_dpdmux(const struct fsl_mc_device *mc_dev) +{ + return mc_dev->dev.type == &fsl_mc_bus_dpdmux_type; +} + static inline bool is_fsl_mc_bus_dpbp(const struct fsl_mc_device *mc_dev) { return mc_dev->dev.type == &fsl_mc_bus_dpbp_type; @@ -484,6 +494,26 @@ static inline bool is_fsl_mc_bus_dpseci(const struct fsl_mc_device *mc_dev) return mc_dev->dev.type == &fsl_mc_bus_dpseci_type; } +static inline bool is_fsl_mc_bus_dpdcei(const struct fsl_mc_device *mc_dev) +{ + return mc_dev->dev.type == &fsl_mc_bus_dpdcei_type; +} + +static inline bool is_fsl_mc_bus_dpaiop(const struct fsl_mc_device *mc_dev) +{ + return mc_dev->dev.type == &fsl_mc_bus_dpaiop_type; +} + +static inline bool is_fsl_mc_bus_dpci(const struct fsl_mc_device *mc_dev) +{ + return mc_dev->dev.type == &fsl_mc_bus_dpci_type; +} + +static inline bool is_fsl_mc_bus_dpdmai(const struct fsl_mc_device *mc_dev) +{ + return mc_dev->dev.type == &fsl_mc_bus_dpdmai_type; +} + /* * Data Path Buffer Pool (DPBP) API * Contains initialization APIs and runtime control APIs for DPBP -- cgit v1.2.3-59-g8ed1b From 9a872def598195d2de4a4a74e17804142e2aa78e Mon Sep 17 00:00:00 2001 From: Laurentiu Tudor Date: Fri, 17 Jul 2020 18:47:59 +0300 Subject: bus: fsl-mc: use raw spin lock to serialize mc cmds Replace the spinlock that serializes the MC commands with a raw spinlock. This is needed for the RT kernel because there are MC commands sent in interrupt context. Signed-off-by: Laurentiu Tudor Signed-off-by: Ioana Ciornei Reviewed-by: Laurentiu Tudor Link: https://lore.kernel.org/r/20200717154800.17169-3-ioana.ciornei@nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/bus/fsl-mc/mc-io.c | 2 +- drivers/bus/fsl-mc/mc-sys.c | 4 ++-- include/linux/fsl/mc.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/bus/fsl-mc/mc-io.c b/drivers/bus/fsl-mc/mc-io.c index 6ae48ad80409..a30b53f1d87d 100644 --- a/drivers/bus/fsl-mc/mc-io.c +++ b/drivers/bus/fsl-mc/mc-io.c @@ -82,7 +82,7 @@ int __must_check fsl_create_mc_io(struct device *dev, mc_io->portal_phys_addr = mc_portal_phys_addr; mc_io->portal_size = mc_portal_size; if (flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL) - spin_lock_init(&mc_io->spinlock); + raw_spin_lock_init(&mc_io->spinlock); else mutex_init(&mc_io->mutex); diff --git a/drivers/bus/fsl-mc/mc-sys.c b/drivers/bus/fsl-mc/mc-sys.c index 3221a7fbaf0a..85a0225db522 100644 --- a/drivers/bus/fsl-mc/mc-sys.c +++ b/drivers/bus/fsl-mc/mc-sys.c @@ -251,7 +251,7 @@ int mc_send_command(struct fsl_mc_io *mc_io, struct fsl_mc_command *cmd) return -EINVAL; if (mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL) - spin_lock_irqsave(&mc_io->spinlock, irq_flags); + raw_spin_lock_irqsave(&mc_io->spinlock, irq_flags); else mutex_lock(&mc_io->mutex); @@ -287,7 +287,7 @@ int mc_send_command(struct fsl_mc_io *mc_io, struct fsl_mc_command *cmd) error = 0; common_exit: if (mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL) - spin_unlock_irqrestore(&mc_io->spinlock, irq_flags); + raw_spin_unlock_irqrestore(&mc_io->spinlock, irq_flags); else mutex_unlock(&mc_io->mutex); diff --git a/include/linux/fsl/mc.h b/include/linux/fsl/mc.h index cdb03aca2aef..a428c61ead6e 100644 --- a/include/linux/fsl/mc.h +++ b/include/linux/fsl/mc.h @@ -339,7 +339,7 @@ struct fsl_mc_io { * This field is only meaningful if the * FSL_MC_IO_ATOMIC_CONTEXT_PORTAL flag is set */ - spinlock_t spinlock; /* serializes mc_send_command() */ + raw_spinlock_t spinlock; /* serializes mc_send_command() */ }; }; -- cgit v1.2.3-59-g8ed1b From 20f7151d63abbb5281ec036197ed822843be1ebf Mon Sep 17 00:00:00 2001 From: Grigore Popescu Date: Fri, 17 Jul 2020 18:48:00 +0300 Subject: bus: fsl-mc: probe the allocatable objects first Because the DPNIs are probed before DPMCPs and other objects that need to be allocated, messages like "No more resources of type X left" are printed by the fsl-mc bus driver. This patch resolves the issue by probing the allocatable objects first and then any other object that may use them. Signed-off-by: Grigore Popescu Signed-off-by: Ioana Ciornei Reviewed-by: Laurentiu Tudor Link: https://lore.kernel.org/r/20200717154800.17169-4-ioana.ciornei@nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/bus/fsl-mc/dprc-driver.c | 57 ++++++++++++++++++++++++++++------------ 1 file changed, 40 insertions(+), 17 deletions(-) diff --git a/drivers/bus/fsl-mc/dprc-driver.c b/drivers/bus/fsl-mc/dprc-driver.c index c8b1c3842c1a..3512d1b95821 100644 --- a/drivers/bus/fsl-mc/dprc-driver.c +++ b/drivers/bus/fsl-mc/dprc-driver.c @@ -27,7 +27,16 @@ static bool fsl_mc_device_match(struct fsl_mc_device *mc_dev, { return mc_dev->obj_desc.id == obj_desc->id && strcmp(mc_dev->obj_desc.type, obj_desc->type) == 0; +} +static bool fsl_mc_obj_desc_is_allocatable(struct fsl_mc_obj_desc *obj) +{ + if (strcmp(obj->type, "dpmcp") == 0 || + strcmp(obj->type, "dpcon") == 0 || + strcmp(obj->type, "dpbp") == 0) + return true; + else + return false; } static int __fsl_mc_device_remove_if_not_in_mc(struct device *dev, void *data) @@ -150,6 +159,27 @@ static void check_plugged_state_change(struct fsl_mc_device *mc_dev, } } +static void fsl_mc_obj_device_add(struct fsl_mc_device *mc_bus_dev, + struct fsl_mc_obj_desc *obj_desc) +{ + int error; + struct fsl_mc_device *child_dev; + + /* + * Check if device is already known to Linux: + */ + child_dev = fsl_mc_device_lookup(obj_desc, mc_bus_dev); + if (child_dev) { + check_plugged_state_change(child_dev, obj_desc); + put_device(&child_dev->dev); + } else { + error = fsl_mc_device_add(obj_desc, NULL, &mc_bus_dev->dev, + &child_dev); + if (error < 0) + return; + } +} + /** * dprc_add_new_devices - Adds devices to the logical bus for a DPRC * @@ -166,30 +196,23 @@ static void dprc_add_new_devices(struct fsl_mc_device *mc_bus_dev, struct fsl_mc_obj_desc *obj_desc_array, int num_child_objects_in_mc) { - int error; int i; + /* probe the allocable objects first */ for (i = 0; i < num_child_objects_in_mc; i++) { - struct fsl_mc_device *child_dev; struct fsl_mc_obj_desc *obj_desc = &obj_desc_array[i]; - if (strlen(obj_desc->type) == 0) - continue; + if (strlen(obj_desc->type) > 0 && + fsl_mc_obj_desc_is_allocatable(obj_desc)) + fsl_mc_obj_device_add(mc_bus_dev, obj_desc); + } - /* - * Check if device is already known to Linux: - */ - child_dev = fsl_mc_device_lookup(obj_desc, mc_bus_dev); - if (child_dev) { - check_plugged_state_change(child_dev, obj_desc); - put_device(&child_dev->dev); - continue; - } + for (i = 0; i < num_child_objects_in_mc; i++) { + struct fsl_mc_obj_desc *obj_desc = &obj_desc_array[i]; - error = fsl_mc_device_add(obj_desc, NULL, &mc_bus_dev->dev, - &child_dev); - if (error < 0) - continue; + if (strlen(obj_desc->type) > 0 && + !fsl_mc_obj_desc_is_allocatable(obj_desc)) + fsl_mc_obj_device_add(mc_bus_dev, obj_desc); } } -- cgit v1.2.3-59-g8ed1b From e24ca92599cb73ea6d2f768632f4e30b6f9983cf Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 23 Jul 2020 19:29:25 +0200 Subject: Revert "mei: Avoid the use of one-element arrays" This reverts commit 3c3b7ddef7879abb2c42422e898145826c79e5f0, as it turns out Tomas made a better series of patches for this same issue. Cc: Gustavo A. R. Silva Cc: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/hbm.c | 4 ++-- drivers/misc/mei/hw.h | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c index f020d5594154..a44094cdbc36 100644 --- a/drivers/misc/mei/hbm.c +++ b/drivers/misc/mei/hbm.c @@ -408,14 +408,14 @@ static int mei_hbm_add_cl_resp(struct mei_device *dev, u8 addr, u8 status) { struct mei_msg_hdr mei_hdr; struct hbm_add_client_response resp; - const size_t len = sizeof(resp); + const size_t len = sizeof(struct hbm_add_client_response); int ret; dev_dbg(dev->dev, "adding client response\n"); mei_hbm_hdr(&mei_hdr, len); - memset(&resp, 0, len); + memset(&resp, 0, sizeof(struct hbm_add_client_response)); resp.hbm_cmd = MEI_HBM_ADD_CLIENT_RES_CMD; resp.me_addr = addr; resp.status = status; diff --git a/drivers/misc/mei/hw.h b/drivers/misc/mei/hw.h index 8c0297f0e7f3..b1a8d5ec88b3 100644 --- a/drivers/misc/mei/hw.h +++ b/drivers/misc/mei/hw.h @@ -346,13 +346,13 @@ struct hbm_add_client_request { * @hbm_cmd: bus message command header * @me_addr: address of the client in ME * @status: if HBMS_SUCCESS then the client can now accept connections. - * @reserved: reserved for alignment. + * @reserved: reserved */ struct hbm_add_client_response { u8 hbm_cmd; u8 me_addr; u8 status; - u8 reserved; + u8 reserved[1]; } __packed; /** @@ -461,7 +461,7 @@ struct hbm_notification { u8 hbm_cmd; u8 me_addr; u8 host_addr; - u8 reserved; + u8 reserved[1]; } __packed; /** -- cgit v1.2.3-59-g8ed1b From df4b37bcc80d236df53dcc71c8bd9928507101ff Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Thu, 23 Jul 2020 17:59:22 +0300 Subject: mei: hbm: use sizeof of variable instead of struct type There is a possibility of bug when variable type has changed but corresponding struct passed to the sizeof has not. Reviewed-by: Gustavo A. R. Silva Signed-off-by: Tomas Winkler Link: https://lore.kernel.org/r/20200723145927.882743-2-tomas.winkler@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/hbm.c | 74 ++++++++++++++++++++++---------------------------- 1 file changed, 32 insertions(+), 42 deletions(-) diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c index a44094cdbc36..308caee86920 100644 --- a/drivers/misc/mei/hbm.c +++ b/drivers/misc/mei/hbm.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (c) 2003-2019, Intel Corporation. All rights reserved. + * Copyright (c) 2003-2020, Intel Corporation. All rights reserved. * Intel Management Engine Interface (Intel MEI) Linux driver */ #include @@ -257,22 +257,21 @@ int mei_hbm_start_wait(struct mei_device *dev) int mei_hbm_start_req(struct mei_device *dev) { struct mei_msg_hdr mei_hdr; - struct hbm_host_version_request start_req; - const size_t len = sizeof(struct hbm_host_version_request); + struct hbm_host_version_request req; int ret; mei_hbm_reset(dev); - mei_hbm_hdr(&mei_hdr, len); + mei_hbm_hdr(&mei_hdr, sizeof(req)); /* host start message */ - memset(&start_req, 0, len); - start_req.hbm_cmd = HOST_START_REQ_CMD; - start_req.host_version.major_version = HBM_MAJOR_VERSION; - start_req.host_version.minor_version = HBM_MINOR_VERSION; + memset(&req, 0, sizeof(req)); + req.hbm_cmd = HOST_START_REQ_CMD; + req.host_version.major_version = HBM_MAJOR_VERSION; + req.host_version.minor_version = HBM_MINOR_VERSION; dev->hbm_state = MEI_HBM_IDLE; - ret = mei_hbm_write_message(dev, &mei_hdr, &start_req); + ret = mei_hbm_write_message(dev, &mei_hdr, &req); if (ret) { dev_err(dev->dev, "version message write failed: ret = %d\n", ret); @@ -295,13 +294,12 @@ static int mei_hbm_dma_setup_req(struct mei_device *dev) { struct mei_msg_hdr mei_hdr; struct hbm_dma_setup_request req; - const size_t len = sizeof(struct hbm_dma_setup_request); unsigned int i; int ret; - mei_hbm_hdr(&mei_hdr, len); + mei_hbm_hdr(&mei_hdr, sizeof(req)); - memset(&req, 0, len); + memset(&req, 0, sizeof(req)); req.hbm_cmd = MEI_HBM_DMA_SETUP_REQ_CMD; for (i = 0; i < DMA_DSCR_NUM; i++) { phys_addr_t paddr; @@ -337,21 +335,19 @@ static int mei_hbm_dma_setup_req(struct mei_device *dev) static int mei_hbm_enum_clients_req(struct mei_device *dev) { struct mei_msg_hdr mei_hdr; - struct hbm_host_enum_request enum_req; - const size_t len = sizeof(struct hbm_host_enum_request); + struct hbm_host_enum_request req; int ret; /* enumerate clients */ - mei_hbm_hdr(&mei_hdr, len); + mei_hbm_hdr(&mei_hdr, sizeof(req)); - memset(&enum_req, 0, len); - enum_req.hbm_cmd = HOST_ENUM_REQ_CMD; - enum_req.flags |= dev->hbm_f_dc_supported ? - MEI_HBM_ENUM_F_ALLOW_ADD : 0; - enum_req.flags |= dev->hbm_f_ie_supported ? + memset(&req, 0, sizeof(req)); + req.hbm_cmd = HOST_ENUM_REQ_CMD; + req.flags |= dev->hbm_f_dc_supported ? MEI_HBM_ENUM_F_ALLOW_ADD : 0; + req.flags |= dev->hbm_f_ie_supported ? MEI_HBM_ENUM_F_IMMEDIATE_ENUM : 0; - ret = mei_hbm_write_message(dev, &mei_hdr, &enum_req); + ret = mei_hbm_write_message(dev, &mei_hdr, &req); if (ret) { dev_err(dev->dev, "enumeration request write failed: ret = %d.\n", ret); @@ -380,7 +376,7 @@ static int mei_hbm_me_cl_add(struct mei_device *dev, mei_me_cl_rm_by_uuid(dev, uuid); - me_cl = kzalloc(sizeof(struct mei_me_client), GFP_KERNEL); + me_cl = kzalloc(sizeof(*me_cl), GFP_KERNEL); if (!me_cl) return -ENOMEM; @@ -408,14 +404,13 @@ static int mei_hbm_add_cl_resp(struct mei_device *dev, u8 addr, u8 status) { struct mei_msg_hdr mei_hdr; struct hbm_add_client_response resp; - const size_t len = sizeof(struct hbm_add_client_response); int ret; dev_dbg(dev->dev, "adding client response\n"); - mei_hbm_hdr(&mei_hdr, len); + mei_hbm_hdr(&mei_hdr, sizeof(resp)); - memset(&resp, 0, sizeof(struct hbm_add_client_response)); + memset(&resp, 0, sizeof(resp)); resp.hbm_cmd = MEI_HBM_ADD_CLIENT_RES_CMD; resp.me_addr = addr; resp.status = status; @@ -469,11 +464,10 @@ int mei_hbm_cl_notify_req(struct mei_device *dev, struct mei_msg_hdr mei_hdr; struct hbm_notification_request req; - const size_t len = sizeof(struct hbm_notification_request); int ret; - mei_hbm_hdr(&mei_hdr, len); - mei_hbm_cl_hdr(cl, MEI_HBM_NOTIFY_REQ_CMD, &req, len); + mei_hbm_hdr(&mei_hdr, sizeof(req)); + mei_hbm_cl_hdr(cl, MEI_HBM_NOTIFY_REQ_CMD, &req, sizeof(req)); req.start = start; @@ -580,8 +574,7 @@ static void mei_hbm_cl_notify(struct mei_device *dev, static int mei_hbm_prop_req(struct mei_device *dev, unsigned long start_idx) { struct mei_msg_hdr mei_hdr; - struct hbm_props_request prop_req; - const size_t len = sizeof(struct hbm_props_request); + struct hbm_props_request req; unsigned long addr; int ret; @@ -591,18 +584,17 @@ static int mei_hbm_prop_req(struct mei_device *dev, unsigned long start_idx) if (addr == MEI_CLIENTS_MAX) { dev->hbm_state = MEI_HBM_STARTED; mei_host_client_init(dev); - return 0; } - mei_hbm_hdr(&mei_hdr, len); + mei_hbm_hdr(&mei_hdr, sizeof(req)); - memset(&prop_req, 0, sizeof(struct hbm_props_request)); + memset(&req, 0, sizeof(req)); - prop_req.hbm_cmd = HOST_CLIENT_PROPERTIES_REQ_CMD; - prop_req.me_addr = addr; + req.hbm_cmd = HOST_CLIENT_PROPERTIES_REQ_CMD; + req.me_addr = addr; - ret = mei_hbm_write_message(dev, &mei_hdr, &prop_req); + ret = mei_hbm_write_message(dev, &mei_hdr, &req); if (ret) { dev_err(dev->dev, "properties request write failed: ret = %d\n", ret); @@ -628,15 +620,14 @@ int mei_hbm_pg(struct mei_device *dev, u8 pg_cmd) { struct mei_msg_hdr mei_hdr; struct hbm_power_gate req; - const size_t len = sizeof(struct hbm_power_gate); int ret; if (!dev->hbm_f_pg_supported) return -EOPNOTSUPP; - mei_hbm_hdr(&mei_hdr, len); + mei_hbm_hdr(&mei_hdr, sizeof(req)); - memset(&req, 0, len); + memset(&req, 0, sizeof(req)); req.hbm_cmd = pg_cmd; ret = mei_hbm_write_message(dev, &mei_hdr, &req); @@ -657,11 +648,10 @@ static int mei_hbm_stop_req(struct mei_device *dev) { struct mei_msg_hdr mei_hdr; struct hbm_host_stop_request req; - const size_t len = sizeof(struct hbm_host_stop_request); - mei_hbm_hdr(&mei_hdr, len); + mei_hbm_hdr(&mei_hdr, sizeof(req)); - memset(&req, 0, len); + memset(&req, 0, sizeof(req)); req.hbm_cmd = HOST_STOP_REQ_CMD; req.reason = DRIVER_STOP_REQUEST; -- cgit v1.2.3-59-g8ed1b From b4a6700c80f9ecbb848fd1fa3ce72062a7a65e93 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Thu, 23 Jul 2020 17:59:23 +0300 Subject: mei: ioctl: use sizeof of variable instead of struct type Use sizeof(connect_data))) instead of sizeof(struct mei_connect_client_data) when copying data between user space and kernel. There is a possibility of bug when variable type has changed but corresponding struct passed to the sizeof has not. Cc: Gustavo A. R. Silva Reviewed-by: Gustavo A. R. Silva Signed-off-by: Tomas Winkler Link: https://lore.kernel.org/r/20200723145927.882743-3-tomas.winkler@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/main.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index f17297f2943d..05e6ad6d4d54 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (c) 2003-2018, Intel Corporation. All rights reserved. + * Copyright (c) 2003-2020, Intel Corporation. All rights reserved. * Intel Management Engine Interface (Intel MEI) Linux driver */ @@ -476,7 +476,7 @@ static long mei_ioctl(struct file *file, unsigned int cmd, unsigned long data) case IOCTL_MEI_CONNECT_CLIENT: dev_dbg(dev->dev, ": IOCTL_MEI_CONNECT_CLIENT.\n"); if (copy_from_user(&connect_data, (char __user *)data, - sizeof(struct mei_connect_client_data))) { + sizeof(connect_data))) { dev_dbg(dev->dev, "failed to copy data from userland\n"); rets = -EFAULT; goto out; @@ -488,7 +488,7 @@ static long mei_ioctl(struct file *file, unsigned int cmd, unsigned long data) /* if all is ok, copying the data back to user. */ if (copy_to_user((char __user *)data, &connect_data, - sizeof(struct mei_connect_client_data))) { + sizeof(connect_data))) { dev_dbg(dev->dev, "failed to copy data to userland\n"); rets = -EFAULT; goto out; -- cgit v1.2.3-59-g8ed1b From 71ae5255f8feaf16ab5a41db5db1c6a3d9c68665 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Thu, 23 Jul 2020 17:59:24 +0300 Subject: mei: bus: use sizeof of variable instead of struct type There is a possibility of bug when variable type has changed but corresponding struct passed to the sizeof has not. Cc: Gustavo A. R. Silva Reviewed-by: Gustavo A. R. Silva Signed-off-by: Tomas Winkler Link: https://lore.kernel.org/r/20200723145927.882743-4-tomas.winkler@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/bus-fixup.c | 23 +++++++++++------------ drivers/misc/mei/bus.c | 2 +- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/drivers/misc/mei/bus-fixup.c b/drivers/misc/mei/bus-fixup.c index 910f059b3384..07ba16d46690 100644 --- a/drivers/misc/mei/bus-fixup.c +++ b/drivers/misc/mei/bus-fixup.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (c) 2013-2019, Intel Corporation. All rights reserved. + * Copyright (c) 2013-2020, Intel Corporation. All rights reserved. * Intel Management Engine Interface (Intel MEI) Linux driver */ @@ -159,17 +159,17 @@ static int mei_osver(struct mei_cl_device *cldev) static int mei_fwver(struct mei_cl_device *cldev) { char buf[MKHI_FWVER_BUF_LEN]; - struct mkhi_msg *req; + struct mkhi_msg req; + struct mkhi_msg *rsp; struct mkhi_fw_ver *fwver; int bytes_recv, ret, i; memset(buf, 0, sizeof(buf)); - req = (struct mkhi_msg *)buf; - req->hdr.group_id = MKHI_GEN_GROUP_ID; - req->hdr.command = MKHI_GEN_GET_FW_VERSION_CMD; + req.hdr.group_id = MKHI_GEN_GROUP_ID; + req.hdr.command = MKHI_GEN_GET_FW_VERSION_CMD; - ret = __mei_cl_send(cldev->cl, buf, sizeof(struct mkhi_msg_hdr), + ret = __mei_cl_send(cldev->cl, (u8 *)&req, sizeof(req), MEI_CL_IO_TX_BLOCKING); if (ret < 0) { dev_err(&cldev->dev, "Could not send ReqFWVersion cmd\n"); @@ -188,7 +188,8 @@ static int mei_fwver(struct mei_cl_device *cldev) return -EIO; } - fwver = (struct mkhi_fw_ver *)req->data; + rsp = (struct mkhi_msg *)buf; + fwver = (struct mkhi_fw_ver *)rsp->data; memset(cldev->bus->fw_ver, 0, sizeof(cldev->bus->fw_ver)); for (i = 0; i < MEI_MAX_FW_VER_BLOCKS; i++) { if ((size_t)bytes_recv < MKHI_FWVER_LEN(i + 1)) @@ -329,16 +330,14 @@ static int mei_nfc_if_version(struct mei_cl *cl, WARN_ON(mutex_is_locked(&bus->device_lock)); - ret = __mei_cl_send(cl, (u8 *)&cmd, sizeof(struct mei_nfc_cmd), - MEI_CL_IO_TX_BLOCKING); + ret = __mei_cl_send(cl, (u8 *)&cmd, sizeof(cmd), MEI_CL_IO_TX_BLOCKING); if (ret < 0) { dev_err(bus->dev, "Could not send IF version cmd\n"); return ret; } /* to be sure on the stack we alloc memory */ - if_version_length = sizeof(struct mei_nfc_reply) + - sizeof(struct mei_nfc_if_version); + if_version_length = sizeof(*reply) + sizeof(*ver); reply = kzalloc(if_version_length, GFP_KERNEL); if (!reply) @@ -352,7 +351,7 @@ static int mei_nfc_if_version(struct mei_cl *cl, goto err; } - memcpy(ver, reply->data, sizeof(struct mei_nfc_if_version)); + memcpy(ver, reply->data, sizeof(*ver)); dev_info(bus->dev, "NFC MEI VERSION: IVN 0x%x Vendor ID 0x%x Type 0x%x\n", ver->fw_ivn, ver->vendor_id, ver->radio_type); diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index f476dbc7252b..a6dfc3ce1db2 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -931,7 +931,7 @@ static struct mei_cl_device *mei_cl_bus_dev_alloc(struct mei_device *bus, struct mei_cl_device *cldev; struct mei_cl *cl; - cldev = kzalloc(sizeof(struct mei_cl_device), GFP_KERNEL); + cldev = kzalloc(sizeof(*cldev), GFP_KERNEL); if (!cldev) return NULL; -- cgit v1.2.3-59-g8ed1b From 4b40b225812d1be14c043fcaefe956856a2b8ab0 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Thu, 23 Jul 2020 17:59:25 +0300 Subject: mei: client: use sizeof of variable instead of struct type There is a possibility of bug when variable type has changed but corresponding struct passed to the sizeof has not. Cc: Gustavo A. R. Silva Reviewed-by: Gustavo A. R. Silva Signed-off-by: Tomas Winkler Link: https://lore.kernel.org/r/20200723145927.882743-5-tomas.winkler@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/client.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index b32c825a0945..2572887d99b6 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (c) 2003-2019, Intel Corporation. All rights reserved. + * Copyright (c) 2003-2020, Intel Corporation. All rights reserved. * Intel Management Engine Interface (Intel MEI) Linux driver */ @@ -369,7 +369,7 @@ static struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, { struct mei_cl_cb *cb; - cb = kzalloc(sizeof(struct mei_cl_cb), GFP_KERNEL); + cb = kzalloc(sizeof(*cb), GFP_KERNEL); if (!cb) return NULL; @@ -552,7 +552,7 @@ int mei_cl_flush_queues(struct mei_cl *cl, const struct file *fp) */ static void mei_cl_init(struct mei_cl *cl, struct mei_device *dev) { - memset(cl, 0, sizeof(struct mei_cl)); + memset(cl, 0, sizeof(*cl)); init_waitqueue_head(&cl->wait); init_waitqueue_head(&cl->rx_wait); init_waitqueue_head(&cl->tx_wait); @@ -575,7 +575,7 @@ struct mei_cl *mei_cl_allocate(struct mei_device *dev) { struct mei_cl *cl; - cl = kmalloc(sizeof(struct mei_cl), GFP_KERNEL); + cl = kmalloc(sizeof(*cl), GFP_KERNEL); if (!cl) return NULL; -- cgit v1.2.3-59-g8ed1b From c614970e3e5f604f7490e73b465ba66f78f9d715 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Thu, 23 Jul 2020 17:59:26 +0300 Subject: mei: hw: use sizeof of variable instead of struct type Use sizeof(*dev) + sizeof(*hw) instead of sizeof(struct mei_device) + sizeof(struct mei_me_hw) There is a possibility of bug when variable type has changed but corresponding struct passed to the sizeof has not. Cc: Gustavo A. R. Silva Reviewed-by: Gustavo A. R. Silva Signed-off-by: Tomas Winkler Link: https://lore.kernel.org/r/20200723145927.882743-6-tomas.winkler@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/hw-me.c | 5 ++--- drivers/misc/mei/hw-txe.c | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index c51d3da8f333..7692b69abcb5 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (c) 2003-2019, Intel Corporation. All rights reserved. + * Copyright (c) 2003-2020, Intel Corporation. All rights reserved. * Intel Management Engine Interface (Intel MEI) Linux driver */ @@ -1600,8 +1600,7 @@ struct mei_device *mei_me_dev_init(struct device *parent, struct mei_me_hw *hw; int i; - dev = devm_kzalloc(parent, sizeof(struct mei_device) + - sizeof(struct mei_me_hw), GFP_KERNEL); + dev = devm_kzalloc(parent, sizeof(*dev) + sizeof(*hw), GFP_KERNEL); if (!dev) return NULL; diff --git a/drivers/misc/mei/hw-txe.c b/drivers/misc/mei/hw-txe.c index 785b260b3ae9..a4e854b9b9e6 100644 --- a/drivers/misc/mei/hw-txe.c +++ b/drivers/misc/mei/hw-txe.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (c) 2013-2019, Intel Corporation. All rights reserved. + * Copyright (c) 2013-2020, Intel Corporation. All rights reserved. * Intel Management Engine Interface (Intel MEI) Linux driver */ @@ -1201,8 +1201,7 @@ struct mei_device *mei_txe_dev_init(struct pci_dev *pdev) struct mei_device *dev; struct mei_txe_hw *hw; - dev = devm_kzalloc(&pdev->dev, sizeof(struct mei_device) + - sizeof(struct mei_txe_hw), GFP_KERNEL); + dev = devm_kzalloc(&pdev->dev, sizeof(*dev) + sizeof(*hw), GFP_KERNEL); if (!dev) return NULL; -- cgit v1.2.3-59-g8ed1b From 92ca3dd4867bafbfd026b06d53737d61ded1bd27 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Thu, 23 Jul 2020 17:59:27 +0300 Subject: mei: hw: don't use one element arrays Replace the single element arrays with a simple value type u8 reserved, even thought is is not used for dynamically sized trailing elements it confuses the effort of replacing one-element arrays with flexible arrays for that purpose. Link: https://github.com/KSPP/linux/issues/79 Cc: Gustavo A. R. Silva Reviewed-by: Gustavo A. R. Silva Signed-off-by: Tomas Winkler Link: https://lore.kernel.org/r/20200723145927.882743-7-tomas.winkler@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/hw.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/misc/mei/hw.h b/drivers/misc/mei/hw.h index b1a8d5ec88b3..26fa92cb7f7a 100644 --- a/drivers/misc/mei/hw.h +++ b/drivers/misc/mei/hw.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (c) 2003-2018, Intel Corporation. All rights reserved + * Copyright (c) 2003-2020, Intel Corporation. All rights reserved * Intel Management Engine Interface (Intel MEI) Linux driver */ @@ -319,7 +319,7 @@ struct hbm_props_response { u8 hbm_cmd; u8 me_addr; u8 status; - u8 reserved[1]; + u8 reserved; struct mei_client_properties client_properties; } __packed; @@ -352,7 +352,7 @@ struct hbm_add_client_response { u8 hbm_cmd; u8 me_addr; u8 status; - u8 reserved[1]; + u8 reserved; } __packed; /** @@ -461,7 +461,7 @@ struct hbm_notification { u8 hbm_cmd; u8 me_addr; u8 host_addr; - u8 reserved[1]; + u8 reserved; } __packed; /** -- cgit v1.2.3-59-g8ed1b From e20e310c8195ef36d46cd4a768a79e1ea93c20a4 Mon Sep 17 00:00:00 2001 From: Jim Cromie Date: Sun, 19 Jul 2020 17:10:41 -0600 Subject: dyndbg-docs: eschew file /full/path query in docs Regarding: commit 2b6783191da7 ("dynamic_debug: add trim_prefix() to provide source-root relative paths") commit a73619a845d5 ("kbuild: use -fmacro-prefix-map to make __FILE__ a relative path") 2nd commit broke dynamic-debug's "file $fullpath" query form, but nobody noticed because 1st commit had trimmed prefixes from control-file output, so the click-copy-pasting of fullpaths into new queries had ceased; that query form became unused. Removing the function is cleanest, but it could be useful in old-compiler corner cases, where __FILE__ still has /full/path, and it safely does nothing otherwize. So instead, quietly deprecate "file /full/path" query form, by removing all /full/paths examples in the docs. I skipped adding a back-compat note. Acked-by: Signed-off-by: Jim Cromie Link: https://lore.kernel.org/r/20200719231058.1586423-2-jim.cromie@gmail.com Signed-off-by: Greg Kroah-Hartman --- Documentation/admin-guide/dynamic-debug-howto.rst | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/Documentation/admin-guide/dynamic-debug-howto.rst b/Documentation/admin-guide/dynamic-debug-howto.rst index 1012bd9305e9..57108f64afc8 100644 --- a/Documentation/admin-guide/dynamic-debug-howto.rst +++ b/Documentation/admin-guide/dynamic-debug-howto.rst @@ -70,10 +70,10 @@ statements via:: nullarbor:~ # cat /dynamic_debug/control # filename:lineno [module]function flags format - /usr/src/packages/BUILD/sgi-enhancednfs-1.4/default/net/sunrpc/svc_rdma.c:323 [svcxprt_rdma]svc_rdma_cleanup =_ "SVCRDMA Module Removed, deregister RPC RDMA transport\012" - /usr/src/packages/BUILD/sgi-enhancednfs-1.4/default/net/sunrpc/svc_rdma.c:341 [svcxprt_rdma]svc_rdma_init =_ "\011max_inline : %d\012" - /usr/src/packages/BUILD/sgi-enhancednfs-1.4/default/net/sunrpc/svc_rdma.c:340 [svcxprt_rdma]svc_rdma_init =_ "\011sq_depth : %d\012" - /usr/src/packages/BUILD/sgi-enhancednfs-1.4/default/net/sunrpc/svc_rdma.c:338 [svcxprt_rdma]svc_rdma_init =_ "\011max_requests : %d\012" + net/sunrpc/svc_rdma.c:323 [svcxprt_rdma]svc_rdma_cleanup =_ "SVCRDMA Module Removed, deregister RPC RDMA transport\012" + net/sunrpc/svc_rdma.c:341 [svcxprt_rdma]svc_rdma_init =_ "\011max_inline : %d\012" + net/sunrpc/svc_rdma.c:340 [svcxprt_rdma]svc_rdma_init =_ "\011sq_depth : %d\012" + net/sunrpc/svc_rdma.c:338 [svcxprt_rdma]svc_rdma_init =_ "\011max_requests : %d\012" ... @@ -93,7 +93,7 @@ the debug statement callsites with any non-default flags:: nullarbor:~ # awk '$3 != "=_"' /dynamic_debug/control # filename:lineno [module]function flags format - /usr/src/packages/BUILD/sgi-enhancednfs-1.4/default/net/sunrpc/svcsock.c:1603 [sunrpc]svc_send p "svc_process: st_sendto returned %d\012" + net/sunrpc/svcsock.c:1603 [sunrpc]svc_send p "svc_process: st_sendto returned %d\012" Command Language Reference ========================== @@ -166,13 +166,12 @@ func func svc_tcp_accept file - The given string is compared against either the full pathname, the - src-root relative pathname, or the basename of the source file of - each callsite. Examples:: + The given string is compared against either the src-root relative + pathname, or the basename of the source file of each callsite. + Examples:: file svcsock.c - file kernel/freezer.c - file /usr/src/packages/BUILD/sgi-enhancednfs-1.4/default/net/sunrpc/svcsock.c + file kernel/freezer.c # ie column 1 of control file module The given string is compared against the module name -- cgit v1.2.3-59-g8ed1b From fa0805207066ce3bdc414fb14fda95a3b7ba87bb Mon Sep 17 00:00:00 2001 From: Jim Cromie Date: Sun, 19 Jul 2020 17:10:42 -0600 Subject: dyndbg-docs: initialization is done early, not arch since cf964976484 in 2012, initialization is done with early_initcall, update the Docs, which still say arch_initcall. Acked-by: Signed-off-by: Jim Cromie Link: https://lore.kernel.org/r/20200719231058.1586423-3-jim.cromie@gmail.com Signed-off-by: Greg Kroah-Hartman --- Documentation/admin-guide/dynamic-debug-howto.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/admin-guide/dynamic-debug-howto.rst b/Documentation/admin-guide/dynamic-debug-howto.rst index 57108f64afc8..1423af580bed 100644 --- a/Documentation/admin-guide/dynamic-debug-howto.rst +++ b/Documentation/admin-guide/dynamic-debug-howto.rst @@ -250,8 +250,8 @@ the syntax described above, but must not exceed 1023 characters. Your bootloader may impose lower limits. These ``dyndbg`` params are processed just after the ddebug tables are -processed, as part of the arch_initcall. Thus you can enable debug -messages in all code run after this arch_initcall via this boot +processed, as part of the early_initcall. Thus you can enable debug +messages in all code run after this early_initcall via this boot parameter. On an x86 system for example ACPI enablement is a subsys_initcall and:: -- cgit v1.2.3-59-g8ed1b From 1ff838487d6fcd013f2bde44c8e486f9d6758271 Mon Sep 17 00:00:00 2001 From: Jim Cromie Date: Sun, 19 Jul 2020 17:10:43 -0600 Subject: dyndbg: drop obsolete comment on ddebug_proc_open commit 4bad78c55002 ("lib/dynamic_debug.c: use seq_open_private() instead of seq_open()")' The commit was one of a tree-wide set which replaced open-coded boilerplate with a single tail-call. It therefore obsoleted the comment about that boilerplate, clean that up now. Acked-by: Signed-off-by: Jim Cromie Link: https://lore.kernel.org/r/20200719231058.1586423-4-jim.cromie@gmail.com Signed-off-by: Greg Kroah-Hartman --- lib/dynamic_debug.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c index 321437bbf87d..2989a590ce9a 100644 --- a/lib/dynamic_debug.c +++ b/lib/dynamic_debug.c @@ -853,13 +853,6 @@ static const struct seq_operations ddebug_proc_seqops = { .stop = ddebug_proc_stop }; -/* - * File_ops->open method for /dynamic_debug/control. Does - * the seq_file setup dance, and also creates an iterator to walk the - * _ddebugs. Note that we create a seq_file always, even for O_WRONLY - * files where it's not needed, as doing so simplifies the ->release - * method. - */ static int ddebug_proc_open(struct inode *inode, struct file *file) { vpr_info("called\n"); -- cgit v1.2.3-59-g8ed1b From 481c0e33f1e71a7ad934bab07b6f3f77243ef0e5 Mon Sep 17 00:00:00 2001 From: Jim Cromie Date: Sun, 19 Jul 2020 17:10:44 -0600 Subject: dyndbg: refine debug verbosity; 1 is basic, 2 more chatty The verbose/debug logging done for `cat $MNT/dynamic_debug/control` is voluminous (2 per control file entry + 2 per PAGE). Moreover, it just prints pointer and sequence, which is not useful to a dyndbg user. So just drop them. Also require verbose>=2 for several other debug printks that are a bit too chatty for typical needs; ddebug_change() prints changes, once per modified callsite. Since queries like "+p" will enable ~2300 callsites in a typical laptop, a user probably doesn't need to see them often. ddebug_exec_queries() still summarizes with verbose=1. ddebug_(add|remove)_module() also print 1 line per action on a module, not needed by typical modprobe user. This leaves verbose=1 better focussed on the >control parsing process. Acked-by: Signed-off-by: Jim Cromie Link: https://lore.kernel.org/r/20200719231058.1586423-5-jim.cromie@gmail.com Signed-off-by: Greg Kroah-Hartman --- lib/dynamic_debug.c | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c index 2989a590ce9a..c97872cffc8e 100644 --- a/lib/dynamic_debug.c +++ b/lib/dynamic_debug.c @@ -105,12 +105,15 @@ static char *ddebug_describe_flags(struct _ddebug *dp, char *buf, return buf; } -#define vpr_info(fmt, ...) \ +#define vnpr_info(lvl, fmt, ...) \ do { \ - if (verbose) \ + if (verbose >= lvl) \ pr_info(fmt, ##__VA_ARGS__); \ } while (0) +#define vpr_info(fmt, ...) vnpr_info(1, fmt, ##__VA_ARGS__) +#define v2pr_info(fmt, ...) vnpr_info(2, fmt, ##__VA_ARGS__) + static void vpr_info_dq(const struct ddebug_query *query, const char *msg) { /* trim any trailing newlines */ @@ -198,7 +201,7 @@ static int ddebug_change(const struct ddebug_query *query, static_branch_enable(&dp->key.dd_key_true); #endif dp->flags = newflags; - vpr_info("changed %s:%d [%s]%s =%s\n", + v2pr_info("changed %s:%d [%s]%s =%s\n", trim_prefix(dp->filename), dp->lineno, dt->mod_name, dp->function, ddebug_describe_flags(dp, flagbuf, @@ -771,8 +774,6 @@ static void *ddebug_proc_start(struct seq_file *m, loff_t *pos) struct _ddebug *dp; int n = *pos; - vpr_info("called m=%p *pos=%lld\n", m, (unsigned long long)*pos); - mutex_lock(&ddebug_lock); if (!n) @@ -795,9 +796,6 @@ static void *ddebug_proc_next(struct seq_file *m, void *p, loff_t *pos) struct ddebug_iter *iter = m->private; struct _ddebug *dp; - vpr_info("called m=%p p=%p *pos=%lld\n", - m, p, (unsigned long long)*pos); - if (p == SEQ_START_TOKEN) dp = ddebug_iter_first(iter); else @@ -818,8 +816,6 @@ static int ddebug_proc_show(struct seq_file *m, void *p) struct _ddebug *dp = p; char flagsbuf[10]; - vpr_info("called m=%p p=%p\n", m, p); - if (p == SEQ_START_TOKEN) { seq_puts(m, "# filename:lineno [module]function flags format\n"); @@ -842,7 +838,6 @@ static int ddebug_proc_show(struct seq_file *m, void *p) */ static void ddebug_proc_stop(struct seq_file *m, void *p) { - vpr_info("called m=%p p=%p\n", m, p); mutex_unlock(&ddebug_lock); } @@ -905,7 +900,7 @@ int ddebug_add_module(struct _ddebug *tab, unsigned int n, list_add_tail(&dt->link, &ddebug_tables); mutex_unlock(&ddebug_lock); - vpr_info("%u debug prints in module %s\n", n, dt->mod_name); + v2pr_info("%u debug prints in module %s\n", n, dt->mod_name); return 0; } @@ -964,7 +959,7 @@ int ddebug_remove_module(const char *mod_name) struct ddebug_table *dt, *nextdt; int ret = -ENOENT; - vpr_info("removing module \"%s\"\n", mod_name); + v2pr_info("removing module \"%s\"\n", mod_name); mutex_lock(&ddebug_lock); list_for_each_entry_safe(dt, nextdt, &ddebug_tables, link) { -- cgit v1.2.3-59-g8ed1b From e5ebffe18e5add85acb23c7a0f74509749967204 Mon Sep 17 00:00:00 2001 From: Jim Cromie Date: Sun, 19 Jul 2020 17:10:45 -0600 Subject: dyndbg: rename __verbose section to __dyndbg dyndbg populates its callsite info into __verbose section, change that to a more specific and descriptive name, __dyndbg. Also, per checkpatch: simplify __attribute(..) to __section(__dyndbg) declaration. and 1 spelling fix, decriptor Acked-by: Signed-off-by: Jim Cromie Link: https://lore.kernel.org/r/20200719231058.1586423-6-jim.cromie@gmail.com Signed-off-by: Greg Kroah-Hartman --- include/asm-generic/vmlinux.lds.h | 6 +++--- include/linux/dynamic_debug.h | 4 ++-- kernel/module.c | 2 +- lib/dynamic_debug.c | 12 ++++++------ 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index db600ef218d7..05af5cef1ad6 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -320,9 +320,9 @@ *(__tracepoints) \ /* implement dynamic printk debug */ \ . = ALIGN(8); \ - __start___verbose = .; \ - KEEP(*(__verbose)) \ - __stop___verbose = .; \ + __start___dyndbg = .; \ + KEEP(*(__dyndbg)) \ + __stop___dyndbg = .; \ LIKELY_PROFILE() \ BRANCH_PROFILE() \ TRACE_PRINTKS() \ diff --git a/include/linux/dynamic_debug.h b/include/linux/dynamic_debug.h index abcd5fde30eb..aa9ff9e1c0b3 100644 --- a/include/linux/dynamic_debug.h +++ b/include/linux/dynamic_debug.h @@ -80,7 +80,7 @@ void __dynamic_ibdev_dbg(struct _ddebug *descriptor, #define DEFINE_DYNAMIC_DEBUG_METADATA(name, fmt) \ static struct _ddebug __aligned(8) \ - __attribute__((section("__verbose"))) name = { \ + __section(__dyndbg) name = { \ .modname = KBUILD_MODNAME, \ .function = __func__, \ .filename = __FILE__, \ @@ -133,7 +133,7 @@ void __dynamic_ibdev_dbg(struct _ddebug *descriptor, /* * "Factory macro" for generating a call to func, guarded by a - * DYNAMIC_DEBUG_BRANCH. The dynamic debug decriptor will be + * DYNAMIC_DEBUG_BRANCH. The dynamic debug descriptor will be * initialized using the fmt argument. The function will be called with * the address of the descriptor as first argument, followed by all * the varargs. Note that fmt is repeated in invocations of this diff --git a/kernel/module.c b/kernel/module.c index aa183c9ac0a2..e7b4ff7e4fd0 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -3237,7 +3237,7 @@ static int find_module_sections(struct module *mod, struct load_info *info) if (section_addr(info, "__obsparm")) pr_warn("%s: Ignoring obsolete parameters\n", mod->name); - info->debug = section_objs(info, "__verbose", + info->debug = section_objs(info, "__dyndbg", sizeof(*info->debug), &info->num_debug); return 0; diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c index c97872cffc8e..66c0bdf06ce7 100644 --- a/lib/dynamic_debug.c +++ b/lib/dynamic_debug.c @@ -39,8 +39,8 @@ #include -extern struct _ddebug __start___verbose[]; -extern struct _ddebug __stop___verbose[]; +extern struct _ddebug __start___dyndbg[]; +extern struct _ddebug __stop___dyndbg[]; struct ddebug_table { struct list_head link; @@ -1019,7 +1019,7 @@ static int __init dynamic_debug_init(void) int n = 0, entries = 0, modct = 0; int verbose_bytes = 0; - if (&__start___verbose == &__stop___verbose) { + if (&__start___dyndbg == &__stop___dyndbg) { if (IS_ENABLED(CONFIG_DYNAMIC_DEBUG)) { pr_warn("_ddebug table is empty in a CONFIG_DYNAMIC_DEBUG build\n"); return 1; @@ -1028,10 +1028,10 @@ static int __init dynamic_debug_init(void) ddebug_init_success = 1; return 0; } - iter = __start___verbose; + iter = __start___dyndbg; modname = iter->modname; iter_start = iter; - for (; iter < __stop___verbose; iter++) { + for (; iter < __stop___dyndbg; iter++) { entries++; verbose_bytes += strlen(iter->modname) + strlen(iter->function) + strlen(iter->filename) + strlen(iter->format); @@ -1054,7 +1054,7 @@ static int __init dynamic_debug_init(void) ddebug_init_success = 1; vpr_info("%d modules, %d entries and %d bytes in ddebug tables, %d bytes in (readonly) verbose section\n", modct, entries, (int)(modct * sizeof(struct ddebug_table)), - verbose_bytes + (int)(__stop___verbose - __start___verbose)); + verbose_bytes + (int)(__stop___dyndbg - __start___dyndbg)); /* apply ddebug_query boot param, dont unload tables on err */ if (ddebug_setup_string[0] != '\0') { -- cgit v1.2.3-59-g8ed1b From 81d0c2c60942cf35daf3fc18d42606f669fcf44c Mon Sep 17 00:00:00 2001 From: Jim Cromie Date: Sun, 19 Jul 2020 17:10:46 -0600 Subject: dyndbg: fix overcounting of ram used by dyndbg during dyndbg init, verbose logging prints its ram overhead. It counted strlens of struct _ddebug's 4 string members, in all callsite entries, which would be approximately correct if each had been mallocd. But they are pointers into shared .rodata; for example, all 10 kobject callsites have identical filename, module values. Its best not to count that memory at all, since we cannot know they were linked in because of CONFIG_DYNAMIC_DEBUG=y, and we want to report a number that reflects what ram is saved by deconfiguring it. Also fix wording and size under-reporting of the __dyndbg section. Heres my overhead, on a virtme-run VM on a fedora-31 laptop: dynamic_debug:dynamic_debug_init: 260 modules, 2479 entries \ and 10400 bytes in ddebug tables, 138824 bytes in __dyndbg section Acked-by: Signed-off-by: Jim Cromie Link: https://lore.kernel.org/r/20200719231058.1586423-7-jim.cromie@gmail.com Signed-off-by: Greg Kroah-Hartman --- lib/dynamic_debug.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c index 66c0bdf06ce7..9b2445507988 100644 --- a/lib/dynamic_debug.c +++ b/lib/dynamic_debug.c @@ -1017,7 +1017,6 @@ static int __init dynamic_debug_init(void) char *cmdline; int ret = 0; int n = 0, entries = 0, modct = 0; - int verbose_bytes = 0; if (&__start___dyndbg == &__stop___dyndbg) { if (IS_ENABLED(CONFIG_DYNAMIC_DEBUG)) { @@ -1033,9 +1032,6 @@ static int __init dynamic_debug_init(void) iter_start = iter; for (; iter < __stop___dyndbg; iter++) { entries++; - verbose_bytes += strlen(iter->modname) + strlen(iter->function) - + strlen(iter->filename) + strlen(iter->format); - if (strcmp(modname, iter->modname)) { modct++; ret = ddebug_add_module(iter_start, n, modname); @@ -1052,9 +1048,9 @@ static int __init dynamic_debug_init(void) goto out_err; ddebug_init_success = 1; - vpr_info("%d modules, %d entries and %d bytes in ddebug tables, %d bytes in (readonly) verbose section\n", + vpr_info("%d modules, %d entries and %d bytes in ddebug tables, %d bytes in __dyndbg section\n", modct, entries, (int)(modct * sizeof(struct ddebug_table)), - verbose_bytes + (int)(__stop___dyndbg - __start___dyndbg)); + (int)(entries * sizeof(struct _ddebug))); /* apply ddebug_query boot param, dont unload tables on err */ if (ddebug_setup_string[0] != '\0') { -- cgit v1.2.3-59-g8ed1b From f678ce8cc3cb2ad29df75d8824c74f36398ba871 Mon Sep 17 00:00:00 2001 From: Jim Cromie Date: Sun, 19 Jul 2020 17:10:47 -0600 Subject: dyndbg: fix a BUG_ON in ddebug_describe_flags ddebug_describe_flags() currently fills a caller provided string buffer, after testing its size (also passed) in a BUG_ON. Fix this by replacing them with a known-big-enough string buffer wrapped in a struct, and passing that instead. Also simplify ddebug_describe_flags() flags parameter from a struct to a member in that struct, and hoist the member deref up to the caller. This makes the function reusable (soon) where flags are unpacked. Acked-by: Signed-off-by: Jim Cromie Link: https://lore.kernel.org/r/20200719231058.1586423-8-jim.cromie@gmail.com Signed-off-by: Greg Kroah-Hartman --- lib/dynamic_debug.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c index 9b2445507988..0cb5679f6c54 100644 --- a/lib/dynamic_debug.c +++ b/lib/dynamic_debug.c @@ -87,22 +87,22 @@ static struct { unsigned flag:8; char opt_char; } opt_array[] = { { _DPRINTK_FLAGS_NONE, '_' }, }; +struct flagsbuf { char buf[ARRAY_SIZE(opt_array)+1]; }; + /* format a string into buf[] which describes the _ddebug's flags */ -static char *ddebug_describe_flags(struct _ddebug *dp, char *buf, - size_t maxlen) +static char *ddebug_describe_flags(unsigned int flags, struct flagsbuf *fb) { - char *p = buf; + char *p = fb->buf; int i; - BUG_ON(maxlen < 6); for (i = 0; i < ARRAY_SIZE(opt_array); ++i) - if (dp->flags & opt_array[i].flag) + if (flags & opt_array[i].flag) *p++ = opt_array[i].opt_char; - if (p == buf) + if (p == fb->buf) *p++ = '_'; *p = '\0'; - return buf; + return fb->buf; } #define vnpr_info(lvl, fmt, ...) \ @@ -147,7 +147,7 @@ static int ddebug_change(const struct ddebug_query *query, struct ddebug_table *dt; unsigned int newflags; unsigned int nfound = 0; - char flagbuf[10]; + struct flagsbuf fbuf; /* search for matching ddebugs */ mutex_lock(&ddebug_lock); @@ -204,8 +204,7 @@ static int ddebug_change(const struct ddebug_query *query, v2pr_info("changed %s:%d [%s]%s =%s\n", trim_prefix(dp->filename), dp->lineno, dt->mod_name, dp->function, - ddebug_describe_flags(dp, flagbuf, - sizeof(flagbuf))); + ddebug_describe_flags(dp->flags, &fbuf)); } } mutex_unlock(&ddebug_lock); @@ -814,7 +813,7 @@ static int ddebug_proc_show(struct seq_file *m, void *p) { struct ddebug_iter *iter = m->private; struct _ddebug *dp = p; - char flagsbuf[10]; + struct flagsbuf flags; if (p == SEQ_START_TOKEN) { seq_puts(m, @@ -825,7 +824,7 @@ static int ddebug_proc_show(struct seq_file *m, void *p) seq_printf(m, "%s:%u [%s]%s =%s \"", trim_prefix(dp->filename), dp->lineno, iter->table->mod_name, dp->function, - ddebug_describe_flags(dp, flagsbuf, sizeof(flagsbuf))); + ddebug_describe_flags(dp->flags, &flags)); seq_escape(m, dp->format, "\t\r\n\""); seq_puts(m, "\"\n"); -- cgit v1.2.3-59-g8ed1b From 0b8f96be9bd41521b6379b403cfce78934f8274c Mon Sep 17 00:00:00 2001 From: Jim Cromie Date: Sun, 19 Jul 2020 17:10:48 -0600 Subject: dyndbg: fix pr_err with empty string this pr_err attempts to print the string after the OP, but the string has been parsed and chopped up, so looks empty. Acked-by: Signed-off-by: Jim Cromie Link: https://lore.kernel.org/r/20200719231058.1586423-9-jim.cromie@gmail.com Signed-off-by: Greg Kroah-Hartman --- lib/dynamic_debug.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c index 0cb5679f6c54..1d25a846553b 100644 --- a/lib/dynamic_debug.c +++ b/lib/dynamic_debug.c @@ -420,7 +420,7 @@ static int ddebug_parse_flags(const char *str, unsigned int *flagsp, } } if (i < 0) { - pr_err("unknown flag '%c' in \"%s\"\n", *str, str); + pr_err("unknown flag '%c'\n", *str); return -EINVAL; } } -- cgit v1.2.3-59-g8ed1b From 9c9d0acbe2793315fa6945a19685ad2a51fb281b Mon Sep 17 00:00:00 2001 From: Jim Cromie Date: Sun, 19 Jul 2020 17:10:49 -0600 Subject: dyndbg: prefer declarative init in caller, to memset in callee ddebug_exec_query declares an auto var, and passes it to ddebug_parse_query, which memsets it before using it. Drop that memset, instead initialize the variable in the caller; let the compiler decide how to do it. Acked-by: Signed-off-by: Jim Cromie Link: https://lore.kernel.org/r/20200719231058.1586423-10-jim.cromie@gmail.com Signed-off-by: Greg Kroah-Hartman --- lib/dynamic_debug.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c index 1d25a846553b..da3ed54a6521 100644 --- a/lib/dynamic_debug.c +++ b/lib/dynamic_debug.c @@ -330,7 +330,6 @@ static int ddebug_parse_query(char *words[], int nwords, pr_err("expecting pairs of match-spec \n"); return -EINVAL; } - memset(query, 0, sizeof(*query)); if (modname) /* support $modname.dyndbg= */ @@ -448,7 +447,7 @@ static int ddebug_parse_flags(const char *str, unsigned int *flagsp, static int ddebug_exec_query(char *query_string, const char *modname) { unsigned int flags = 0, mask = 0; - struct ddebug_query query; + struct ddebug_query query = {}; #define MAXWORDS 9 int nwords, nfound; char *words[MAXWORDS]; -- cgit v1.2.3-59-g8ed1b From 47e9f5a82329696b31e7e0672653954f6e9b0f54 Mon Sep 17 00:00:00 2001 From: Jim Cromie Date: Sun, 19 Jul 2020 17:10:50 -0600 Subject: dyndbg: make ddebug_tables list LIFO for add/remove_module loadable modules are the last in on this list, and are the only modules that could be removed. ddebug_remove_module() searches from head, but ddebug_add_module() uses list_add_tail(). Change it to list_add() for a micro-optimization. Acked-by: Signed-off-by: Jim Cromie Link: https://lore.kernel.org/r/20200719231058.1586423-11-jim.cromie@gmail.com Signed-off-by: Greg Kroah-Hartman --- lib/dynamic_debug.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c index da3ed54a6521..e879af4e66e0 100644 --- a/lib/dynamic_debug.c +++ b/lib/dynamic_debug.c @@ -895,7 +895,7 @@ int ddebug_add_module(struct _ddebug *tab, unsigned int n, dt->ddebugs = tab; mutex_lock(&ddebug_lock); - list_add_tail(&dt->link, &ddebug_tables); + list_add(&dt->link, &ddebug_tables); mutex_unlock(&ddebug_lock); v2pr_info("%u debug prints in module %s\n", n, dt->mod_name); -- cgit v1.2.3-59-g8ed1b From f62fc08fdc51d18292eb13bd345dfb159f024746 Mon Sep 17 00:00:00 2001 From: Jim Cromie Date: Sun, 19 Jul 2020 17:10:51 -0600 Subject: dyndbg: use gcc ?: to reduce word count reduce word count via gcc ?: extension, no actual code change. Acked-by: Signed-off-by: Jim Cromie Link: https://lore.kernel.org/r/20200719231058.1586423-12-jim.cromie@gmail.com Signed-off-by: Greg Kroah-Hartman --- lib/dynamic_debug.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c index e879af4e66e0..6d0159075308 100644 --- a/lib/dynamic_debug.c +++ b/lib/dynamic_debug.c @@ -127,10 +127,10 @@ static void vpr_info_dq(const struct ddebug_query *query, const char *msg) vpr_info("%s: func=\"%s\" file=\"%s\" module=\"%s\" format=\"%.*s\" lineno=%u-%u\n", msg, - query->function ? query->function : "", - query->filename ? query->filename : "", - query->module ? query->module : "", - fmtlen, query->format ? query->format : "", + query->function ?: "", + query->filename ?: "", + query->module ?: "", + fmtlen, query->format ?: "", query->first_lineno, query->last_lineno); } -- cgit v1.2.3-59-g8ed1b From 8037072d8139da435f3fcf9369a1ccc388431a2a Mon Sep 17 00:00:00 2001 From: Jim Cromie Date: Sun, 19 Jul 2020 17:10:52 -0600 Subject: dyndbg: refactor parse_linerange out of ddebug_parse_query Make the code-block reusable to later handle "file foo.c:101-200" etc. This is a 99% code move, with reindent, function wrap&call, +pr_debug. no functional changes. Acked-by: Signed-off-by: Jim Cromie Link: https://lore.kernel.org/r/20200719231058.1586423-13-jim.cromie@gmail.com Signed-off-by: Greg Kroah-Hartman --- lib/dynamic_debug.c | 63 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 36 insertions(+), 27 deletions(-) diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c index 6d0159075308..7a66d5e07f41 100644 --- a/lib/dynamic_debug.c +++ b/lib/dynamic_debug.c @@ -291,6 +291,41 @@ static inline int parse_lineno(const char *str, unsigned int *val) return 0; } +static int parse_linerange(struct ddebug_query *query, const char *first) +{ + char *last = strchr(first, '-'); + + if (query->first_lineno || query->last_lineno) { + pr_err("match-spec: line used 2x\n"); + return -EINVAL; + } + if (last) + *last++ = '\0'; + if (parse_lineno(first, &query->first_lineno) < 0) + return -EINVAL; + if (last) { + /* range - */ + if (parse_lineno(last, &query->last_lineno) < 0) + return -EINVAL; + + /* special case for last lineno not specified */ + if (query->last_lineno == 0) + query->last_lineno = UINT_MAX; + + if (query->last_lineno < query->first_lineno) { + pr_err("last-line:%d < 1st-line:%d\n", + query->last_lineno, + query->first_lineno); + return -EINVAL; + } + } else { + query->last_lineno = query->first_lineno; + } + vpr_info("parsed line %d-%d\n", query->first_lineno, + query->last_lineno); + return 0; +} + static int check_set(const char **dest, char *src, char *name) { int rc = 0; @@ -348,34 +383,8 @@ static int ddebug_parse_query(char *words[], int nwords, UNESCAPE_SPECIAL); rc = check_set(&query->format, words[i+1], "format"); } else if (!strcmp(words[i], "line")) { - char *first = words[i+1]; - char *last = strchr(first, '-'); - if (query->first_lineno || query->last_lineno) { - pr_err("match-spec: line used 2x\n"); - return -EINVAL; - } - if (last) - *last++ = '\0'; - if (parse_lineno(first, &query->first_lineno) < 0) + if (parse_linerange(query, words[i+1])) return -EINVAL; - if (last) { - /* range - */ - if (parse_lineno(last, &query->last_lineno) < 0) - return -EINVAL; - - /* special case for last lineno not specified */ - if (query->last_lineno == 0) - query->last_lineno = UINT_MAX; - - if (query->last_lineno < query->first_lineno) { - pr_err("last-line:%d < 1st-line:%d\n", - query->last_lineno, - query->first_lineno); - return -EINVAL; - } - } else { - query->last_lineno = query->first_lineno; - } } else { pr_err("unknown keyword \"%s\"\n", words[i]); return -EINVAL; -- cgit v1.2.3-59-g8ed1b From aaebe329bff0d84e04e74915a828b12a40b55456 Mon Sep 17 00:00:00 2001 From: Jim Cromie Date: Sun, 19 Jul 2020 17:10:53 -0600 Subject: dyndbg: accept 'file foo.c:func1' and 'file foo.c:10-100' Accept these additional query forms: echo "file $filestr +_" > control path/to/file.c:100 # as from control, column 1 path/to/file.c:1-100 # or any legal line-range path/to/file.c:func_A # as from an editor/browser path/to/file.c:drm_* # wildcards still work path/to/file.c:*_foo # lead wildcard too 1st 2 examples are treated as line-ranges, 3-5 are treated as func's Doc these changes, and sprinkle in a few extra wild-card examples and trailing # explanation texts. Acked-by: Signed-off-by: Jim Cromie Link: https://lore.kernel.org/r/20200719231058.1586423-14-jim.cromie@gmail.com Signed-off-by: Greg Kroah-Hartman --- Documentation/admin-guide/dynamic-debug-howto.rst | 5 +++++ lib/dynamic_debug.c | 18 +++++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/Documentation/admin-guide/dynamic-debug-howto.rst b/Documentation/admin-guide/dynamic-debug-howto.rst index 1423af580bed..6c04aea8f4cd 100644 --- a/Documentation/admin-guide/dynamic-debug-howto.rst +++ b/Documentation/admin-guide/dynamic-debug-howto.rst @@ -164,6 +164,7 @@ func of each callsite. Example:: func svc_tcp_accept + func *recv* # in rfcomm, bluetooth, ping, tcp file The given string is compared against either the src-root relative @@ -172,6 +173,9 @@ file file svcsock.c file kernel/freezer.c # ie column 1 of control file + file drivers/usb/* # all callsites under it + file inode.c:start_* # parse :tail as a func (above) + file inode.c:1-100 # parse :tail as a line-range (above) module The given string is compared against the module name @@ -181,6 +185,7 @@ module module sunrpc module nfsd + module drm* # both drm, drm_kms_helper format The given string is searched for in the dynamic debug format diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c index 7a66d5e07f41..7eb963b1bd11 100644 --- a/lib/dynamic_debug.c +++ b/lib/dynamic_debug.c @@ -359,6 +359,7 @@ static int ddebug_parse_query(char *words[], int nwords, { unsigned int i; int rc = 0; + char *fline; /* check we have an even number of words */ if (nwords % 2 != 0) { @@ -374,7 +375,22 @@ static int ddebug_parse_query(char *words[], int nwords, if (!strcmp(words[i], "func")) { rc = check_set(&query->function, words[i+1], "func"); } else if (!strcmp(words[i], "file")) { - rc = check_set(&query->filename, words[i+1], "file"); + if (check_set(&query->filename, words[i+1], "file")) + return -EINVAL; + + /* tail :$info is function or line-range */ + fline = strchr(query->filename, ':'); + if (!fline) + break; + *fline++ = '\0'; + if (isalpha(*fline) || *fline == '*' || *fline == '?') { + /* take as function name */ + if (check_set(&query->function, fline, "func")) + return -EINVAL; + } else { + if (parse_linerange(query, fline)) + return -EINVAL; + } } else if (!strcmp(words[i], "module")) { rc = check_set(&query->module, words[i+1], "module"); } else if (!strcmp(words[i], "format")) { -- cgit v1.2.3-59-g8ed1b From 14775b04964264189caa4a0862eac05dab8c0502 Mon Sep 17 00:00:00 2001 From: Jim Cromie Date: Sun, 19 Jul 2020 17:10:54 -0600 Subject: dyndbg: accept query terms like file=bar and module=foo Current code expects "keyword" "arg" as 2 words, space separated. Change to also accept "keyword=arg" form as well, and drop !(nwords%2) requirement. Then in rest of function, use new keyword, arg variables instead of word[i], word[i+1] Acked-by: Signed-off-by: Jim Cromie Link: https://lore.kernel.org/r/20200719231058.1586423-15-jim.cromie@gmail.com Signed-off-by: Greg Kroah-Hartman --- Documentation/admin-guide/dynamic-debug-howto.rst | 1 + lib/dynamic_debug.c | 53 ++++++++++++++--------- 2 files changed, 34 insertions(+), 20 deletions(-) diff --git a/Documentation/admin-guide/dynamic-debug-howto.rst b/Documentation/admin-guide/dynamic-debug-howto.rst index 6c04aea8f4cd..e5a8def45f3f 100644 --- a/Documentation/admin-guide/dynamic-debug-howto.rst +++ b/Documentation/admin-guide/dynamic-debug-howto.rst @@ -156,6 +156,7 @@ against. Possible keywords are::: ``line-range`` cannot contain space, e.g. "1-30" is valid range but "1 - 30" is not. + ``module=foo`` combined keyword=value form is interchangably accepted The meanings of each keyword are: diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c index 7eb963b1bd11..fad6c34c930d 100644 --- a/lib/dynamic_debug.c +++ b/lib/dynamic_debug.c @@ -341,7 +341,8 @@ static int check_set(const char **dest, char *src, char *name) /* * Parse words[] as a ddebug query specification, which is a series - * of (keyword, value) pairs chosen from these possibilities: + * of (keyword, value) pairs or combined keyword=value terms, + * chosen from these possibilities: * * func * file @@ -360,22 +361,34 @@ static int ddebug_parse_query(char *words[], int nwords, unsigned int i; int rc = 0; char *fline; - - /* check we have an even number of words */ - if (nwords % 2 != 0) { - pr_err("expecting pairs of match-spec \n"); - return -EINVAL; - } + char *keyword, *arg; if (modname) /* support $modname.dyndbg= */ query->module = modname; - for (i = 0; i < nwords; i += 2) { - if (!strcmp(words[i], "func")) { - rc = check_set(&query->function, words[i+1], "func"); - } else if (!strcmp(words[i], "file")) { - if (check_set(&query->filename, words[i+1], "file")) + for (i = 0; i < nwords; i++) { + /* accept keyword=arg */ + vpr_info("%d w:%s\n", i, words[i]); + + keyword = words[i]; + arg = strchr(keyword, '='); + if (arg) { + *arg++ = '\0'; + } else { + i++; /* next word is arg */ + if (!(i < nwords)) { + pr_err("missing arg to keyword: %s\n", keyword); + return -EINVAL; + } + arg = words[i]; + } + vpr_info("%d key:%s arg:%s\n", i, keyword, arg); + + if (!strcmp(keyword, "func")) { + rc = check_set(&query->function, arg, "func"); + } else if (!strcmp(keyword, "file")) { + if (check_set(&query->filename, arg, "file")) return -EINVAL; /* tail :$info is function or line-range */ @@ -391,18 +404,18 @@ static int ddebug_parse_query(char *words[], int nwords, if (parse_linerange(query, fline)) return -EINVAL; } - } else if (!strcmp(words[i], "module")) { - rc = check_set(&query->module, words[i+1], "module"); - } else if (!strcmp(words[i], "format")) { - string_unescape_inplace(words[i+1], UNESCAPE_SPACE | + } else if (!strcmp(keyword, "module")) { + rc = check_set(&query->module, arg, "module"); + } else if (!strcmp(keyword, "format")) { + string_unescape_inplace(arg, UNESCAPE_SPACE | UNESCAPE_OCTAL | UNESCAPE_SPECIAL); - rc = check_set(&query->format, words[i+1], "format"); - } else if (!strcmp(words[i], "line")) { - if (parse_linerange(query, words[i+1])) + rc = check_set(&query->format, arg, "format"); + } else if (!strcmp(keyword, "line")) { + if (parse_linerange(query, arg)) return -EINVAL; } else { - pr_err("unknown keyword \"%s\"\n", words[i]); + pr_err("unknown keyword \"%s\"\n", keyword); return -EINVAL; } if (rc) -- cgit v1.2.3-59-g8ed1b From 84da83a6ffc0b730f64604d869e0697ad106fd42 Mon Sep 17 00:00:00 2001 From: Jim Cromie Date: Sun, 19 Jul 2020 17:10:55 -0600 Subject: dyndbg: combine flags & mask into a struct, simplify with it flags & mask are used together everywhere, and are passed around together between multiple functions; they belong together in a struct, call that struct flag_settings. Use struct flag_settings to rework 3 functions: - ddebug_exec_query - declares query and flag-settings, calls other 2, passing flags - ddebug_parse_flags - fills flag_settings and returns - ddebug_change - test all callsites against query, modify passing sites. benefits: - bit-banging always needs flags & mask, best together. - simpler function signatures - 1 less parameter, less stack overhead no functional changes Acked-by: Signed-off-by: Jim Cromie Link: https://lore.kernel.org/r/20200719231058.1586423-16-jim.cromie@gmail.com Signed-off-by: Greg Kroah-Hartman --- lib/dynamic_debug.c | 45 ++++++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c index fad6c34c930d..c983049e986d 100644 --- a/lib/dynamic_debug.c +++ b/lib/dynamic_debug.c @@ -62,6 +62,11 @@ struct ddebug_iter { unsigned int idx; }; +struct flag_settings { + unsigned int flags; + unsigned int mask; +}; + static DEFINE_MUTEX(ddebug_lock); static LIST_HEAD(ddebug_tables); static int verbose; @@ -141,7 +146,7 @@ static void vpr_info_dq(const struct ddebug_query *query, const char *msg) * logs the changes. Takes ddebug_lock. */ static int ddebug_change(const struct ddebug_query *query, - unsigned int flags, unsigned int mask) + struct flag_settings *modifiers) { int i; struct ddebug_table *dt; @@ -190,14 +195,14 @@ static int ddebug_change(const struct ddebug_query *query, nfound++; - newflags = (dp->flags & mask) | flags; + newflags = (dp->flags & modifiers->mask) | modifiers->flags; if (newflags == dp->flags) continue; #ifdef CONFIG_JUMP_LABEL if (dp->flags & _DPRINTK_FLAGS_PRINT) { - if (!(flags & _DPRINTK_FLAGS_PRINT)) + if (!(modifiers->flags & _DPRINTK_FLAGS_PRINT)) static_branch_disable(&dp->key.dd_key_true); - } else if (flags & _DPRINTK_FLAGS_PRINT) + } else if (modifiers->flags & _DPRINTK_FLAGS_PRINT) static_branch_enable(&dp->key.dd_key_true); #endif dp->flags = newflags; @@ -431,11 +436,9 @@ static int ddebug_parse_query(char *words[], int nwords, * flags fields of matched _ddebug's. Returns 0 on success * or <0 on error. */ -static int ddebug_parse_flags(const char *str, unsigned int *flagsp, - unsigned int *maskp) +static int ddebug_parse_flags(const char *str, struct flag_settings *modifiers) { - unsigned flags = 0; - int op = '=', i; + int op, i; switch (*str) { case '+': @@ -452,7 +455,7 @@ static int ddebug_parse_flags(const char *str, unsigned int *flagsp, for (; *str ; ++str) { for (i = ARRAY_SIZE(opt_array) - 1; i >= 0; i--) { if (*str == opt_array[i].opt_char) { - flags |= opt_array[i].flag; + modifiers->flags |= opt_array[i].flag; break; } } @@ -461,30 +464,30 @@ static int ddebug_parse_flags(const char *str, unsigned int *flagsp, return -EINVAL; } } - vpr_info("flags=0x%x\n", flags); + vpr_info("flags=0x%x\n", modifiers->flags); - /* calculate final *flagsp, *maskp according to mask and op */ + /* calculate final flags, mask based upon op */ switch (op) { case '=': - *maskp = 0; - *flagsp = flags; + /* modifiers->flags already set */ + modifiers->mask = 0; break; case '+': - *maskp = ~0U; - *flagsp = flags; + modifiers->mask = ~0U; break; case '-': - *maskp = ~flags; - *flagsp = 0; + modifiers->mask = ~modifiers->flags; + modifiers->flags = 0; break; } - vpr_info("*flagsp=0x%x *maskp=0x%x\n", *flagsp, *maskp); + vpr_info("*flagsp=0x%x *maskp=0x%x\n", modifiers->flags, modifiers->mask); + return 0; } static int ddebug_exec_query(char *query_string, const char *modname) { - unsigned int flags = 0, mask = 0; + struct flag_settings modifiers = {}; struct ddebug_query query = {}; #define MAXWORDS 9 int nwords, nfound; @@ -496,7 +499,7 @@ static int ddebug_exec_query(char *query_string, const char *modname) return -EINVAL; } /* check flags 1st (last arg) so query is pairs of spec,val */ - if (ddebug_parse_flags(words[nwords-1], &flags, &mask)) { + if (ddebug_parse_flags(words[nwords-1], &modifiers)) { pr_err("flags parse failed\n"); return -EINVAL; } @@ -505,7 +508,7 @@ static int ddebug_exec_query(char *query_string, const char *modname) return -EINVAL; } /* actually go and implement the change */ - nfound = ddebug_change(&query, flags, mask); + nfound = ddebug_change(&query, &modifiers); vpr_info_dq(&query, nfound ? "applied" : "no-match"); return nfound; -- cgit v1.2.3-59-g8ed1b From 4b334484fa7f44baa369db0bbab534f853520f64 Mon Sep 17 00:00:00 2001 From: Jim Cromie Date: Sun, 19 Jul 2020 17:10:56 -0600 Subject: dyndbg: allow anchored match on format query term This should work: echo module=amd* format=^[IF_TRACE]: +p >/proc/dynamic_debug/control consider drivers/gpu/drm/amd/display/include/logger_types.h: It has 11 defines like: #define DC_LOG_IF_TRACE(...) pr_debug("[IF_TRACE]:"__VA_ARGS__) These defines are used 804 times at recent count; they are a good use case to evaluate existing format-message based classifications of *pr_debug*. Those macros prefix the supplied format with a fixed string, I'd expect most existing message classification schemes to do something similar. Hence we want to be able to anchor our match to the beginning of the format string, allowing easy construction of clear and precise queries, leveraging the existing classification scheme to enable and disable those callsites. Note that unlike other search terms, formats are implicitly floating substring matches, without the need for explicit wildcards. This makes no attempt at wider regex features, just the one we need. TLDR: Using the anchor also means the []s are less helpful for disamiguating the prefix from a random in-message occurrence, allowing shorter prefixes. Acked-by: Signed-off-by: Jim Cromie Link: https://lore.kernel.org/r/20200719231058.1586423-17-jim.cromie@gmail.com Signed-off-by: Greg Kroah-Hartman --- lib/dynamic_debug.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c index c983049e986d..c72bb4d2eb7e 100644 --- a/lib/dynamic_debug.c +++ b/lib/dynamic_debug.c @@ -181,9 +181,16 @@ static int ddebug_change(const struct ddebug_query *query, continue; /* match against the format */ - if (query->format && - !strstr(dp->format, query->format)) - continue; + if (query->format) { + if (*query->format == '^') { + char *p; + /* anchored search. match must be at beginning */ + p = strstr(dp->format, query->format+1); + if (p != dp->format) + continue; + } else if (!strstr(dp->format, query->format)) + continue; + } /* match against the line number range */ if (query->first_lineno && -- cgit v1.2.3-59-g8ed1b From 5aa9ffbbaeb3aba22a5d2b6a6eead1f0bcd38254 Mon Sep 17 00:00:00 2001 From: Jim Cromie Date: Sun, 19 Jul 2020 17:10:57 -0600 Subject: dyndbg: shorten our logging prefix, drop __func__ For log-message output, reduce column space consumed by current pr_fmt by dropping __func__ and shortening "dynamic_debug" to "dyndbg". This improves readability on narrow consoles, and better matches other kernel boot info messages. Acked-by: Signed-off-by: Jim Cromie Link: https://lore.kernel.org/r/20200719231058.1586423-18-jim.cromie@gmail.com Signed-off-by: Greg Kroah-Hartman --- lib/dynamic_debug.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c index c72bb4d2eb7e..e87b630bd793 100644 --- a/lib/dynamic_debug.c +++ b/lib/dynamic_debug.c @@ -11,7 +11,7 @@ * Copyright (C) 2013 Du, Changbin */ -#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__ +#define pr_fmt(fmt) "dyndbg: " fmt #include #include -- cgit v1.2.3-59-g8ed1b From 4c0d77828d4f17ecae3e662728b997c0f0276204 Mon Sep 17 00:00:00 2001 From: Jim Cromie Date: Sun, 19 Jul 2020 17:10:58 -0600 Subject: dyndbg: export ddebug_exec_queries Export ddebug_exec_queries() for use by modules. This will allow module authors to control all their *pr_debug*s dynamically. And since ddebug_exec_queries() is what implements "echo $query >control", it gives the same per-callsite control. Virtues of this: - simplicity. just an export. - full control over any/all subsets of callsites. - same "query/command-string" in code and console - full callsite selectivity with module file line format Format in particular deserves special attention; it is where low-hanging fruit will be found. Consider: drivers/gpu/drm/amd/display/include/logger_types.h: #define DC_LOG_SURFACE(...) pr_debug("[SURFACE]:"__VA_ARGS__) #define DC_LOG_HW_LINK_TRAINING(...) pr_debug("[HW_LINK_TRAINING]:"__VA_ARGS__) .. 9 more .. Thats 11 string prefixes, used in 804 places in drivers/gpu/** Clearly this is a systematized classification of those callsites. And one I'd expect to see repeated often. Using ddebug_exec_queries(), authors can select on those prefixes as a unitary set, equivalent to: echo "module=MODULE_NAME format=^[SURFACE]: +p" >control Trivially, those sets can be subsected with the other query terms too, say file=foo, should the author see fit. Perhaps as important, users can modify the set of enabled callsites, presumably to aid debugging by enabling helpful debug callsites, and disabling those that just clutter the info. Authors could even alter [fmlt] flags, though I dont see a good reason why they would. Perhaps harnessed by bug-logging automation to get fuller, or more minimal bug-reports. DRM drm has both drm.debug, which defines 32 categories of drm_printk logging, and entirely separate uses of pr_debug, which are dynamic on this i915 laptop, running mainline. So I can observe and report on both. The i915 driver has 118 dyndbg callsites, with following "classifications" defined in drivers/gpu/drm/i915/gvt/** $ grep 915 /proc/dynamic_debug/control | cut -d= -f2 | cut -d: -f1,2 | sort -u _ "gvt: cmd _ "gvt: core _ "gvt: dpy _ "gvt: el _ "gvt: irq _ "gvt: mm _ "gvt: mmio _ "gvt: render _ "gvt: sched _ "%s for root hub!\012" _ "Vendor defined info completion code %u\012" This classification is entirely out-of-band for control by drm.debug, and is only available to root user at the console. But module authors can activate them with ddebug_exec_queries(sprintf("format=^%s +p")), and then decide how to expose the groups to the user for max utility. drm.debug drm.debug has 32 bit-flags, and matching enum drm_debug_category values to classify the ~2943 DRM_DEBUG*() callsites in drivers/gpu The drm.debug callback could invoke ddebug_exec_queries() with 32 different hardcoded query strings, needing only (bit) ? " +p" : " -p" added. I briefly enabled drm.debug=0xff on my i915 laptop, which yielded these unique prefixes: (dmesg | cut -c17- | cut -d\] -f1 | sort -u) [drm:drm_atomic_check_only [drm [drm:drm_atomic_get_crtc_state [drm [drm:drm_atomic_get_plane_state [drm [drm:drm_atomic_nonblocking_commit [drm [drm:drm_atomic_set_fb_for_plane [drm [drm:drm_atomic_state_default_clear [drm [drm:__drm_atomic_state_free [drm [drm:drm_atomic_state_init [drm [drm:drm_crtc_vblank_helper_get_vblank_timestamp_internal [drm [drm:drm_handle_vblank [drm [drm:drm_ioctl [drm [drm:drm_mode_addfb2 [drm [drm:drm_mode_object_get [drm [drm:drm_mode_object_put.part.0 [drm [drm:drm_update_vblank_count [drm [drm:drm_vblank_enable [drm [drm:drm_vblank_restore [drm [drm:vblank_disable_fn [drm i915 0000:00:02.0: [drm:gen9_set_dc_state [i915 i915 0000:00:02.0: [drm:intel_atomic_get_global_obj_state [i915 i915 0000:00:02.0: [drm:__intel_display_power_get_domain.part.0 [i915 i915 0000:00:02.0: [drm:__intel_display_power_put_domain [i915 i915 0000:00:02.0: [drm:intel_plane_atomic_calc_changes [i915 i915 0000:00:02.0: [drm:skl_enable_dc6 [i915 Several good format=^prefixes are apparent there, and some misses. ^[drm:drm_atomic_ # misses: [drm:__drm_atomic_state_free [drm ^[drm:drm_ioctl ^[drm:drm_mode ^[drm:drm_vblank_ # misses: [drm:drm_update_vblank_count & [drm:vblank_disable_fn Its not a perfect 1:1 single format-match per class, but the misses above can be covered with 1 & 2 additional queries, which can be concatenated together with ";" separators and submitted with 1 call. Benefits: For drm, adapting DRM_DEBUG to use dynamic-debug inside could replicate (and thereby obsolete) lots of bit-checking in current DRM_DEBUG callsites, at least with JUMP_LABEL optimized code. ddebug_exec_queries() and a handful of fixed query-strings can select and thereby control the already classified callsites. With the classes mapped to queries, the enum type and parameter can be eliminated (folded away with macro magic), at least for DYNAMIC_DEBUG & JUMP_LABEL builds. Is it safe ? ddebug_exec_queries() is currently exposed to user space in several limited ways; 1 it is called from module-load callback, where it implements the $modname.dyndbg=+p "fake" parameter provided to all modules. 2 it handles query input via >control directly IOW, it is "fully" exposed to local root user; exposing the same functionality to other kernel modules is no additional risk. The other standard issue to check is locking: dyndbg has a single mutex, taken by ddebug_change to handle >control, and by ddebug_proc_(start|stop) to span `cat control`. Queries submitted via export will typically have module specified, which dramatically cuts the scan by ddebug_change vs "module=* +p". ISTM this proposed export presents no locking problems. TLDR; It would be interesting to see how drm.dyndbg=$QUERY and drm.debug=$HEXY would interact; it might be order dependent, as if given as modprobe args or in /etc/modprobe.d/ Acked-by: Signed-off-by: Jim Cromie Link: https://lore.kernel.org/r/20200719231058.1586423-19-jim.cromie@gmail.com Signed-off-by: Greg Kroah-Hartman --- lib/dynamic_debug.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c index e87b630bd793..1d012e597cc3 100644 --- a/lib/dynamic_debug.c +++ b/lib/dynamic_debug.c @@ -525,7 +525,7 @@ static int ddebug_exec_query(char *query_string, const char *modname) last error or number of matching callsites. Module name is either in param (for boot arg) or perhaps in query string. */ -static int ddebug_exec_queries(char *query, const char *modname) +int ddebug_exec_queries(char *query, const char *modname) { char *split; int i, errs = 0, exitcode = 0, rc, nfound = 0; @@ -557,6 +557,7 @@ static int ddebug_exec_queries(char *query, const char *modname) return exitcode; return nfound; } +EXPORT_SYMBOL_GPL(ddebug_exec_queries); #define PREFIX_SIZE 64 -- cgit v1.2.3-59-g8ed1b From 0b168c8f1d21f87003fb28b4c87c32335d7fc94b Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Mon, 15 Jun 2020 19:25:57 +0300 Subject: habanalabs: remove rate limiters from GAUDI We no longer need to initialize the rate limiters in GAUDI A1. Reviewed-by: Omer Shpigelman Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/gaudi/gaudi.c | 126 +------------------------------ drivers/misc/habanalabs/habanalabs_drv.c | 1 - 2 files changed, 1 insertion(+), 126 deletions(-) diff --git a/drivers/misc/habanalabs/gaudi/gaudi.c b/drivers/misc/habanalabs/gaudi/gaudi.c index 7dd99ec41627..3ee104b7cb8c 100644 --- a/drivers/misc/habanalabs/gaudi/gaudi.c +++ b/drivers/misc/habanalabs/gaudi/gaudi.c @@ -1638,8 +1638,8 @@ static void gaudi_init_hbm_cred(struct hl_device *hdev) uint32_t hbm0_wr, hbm1_wr, hbm0_rd, hbm1_rd; hbm0_wr = 0x33333333; - hbm1_wr = 0x33333333; hbm0_rd = 0x77777777; + hbm1_wr = 0x55555555; hbm1_rd = 0xDDDDDDDD; WREG32(mmDMA_IF_E_N_HBM0_WR_CRED_CNT, hbm0_wr); @@ -1689,125 +1689,6 @@ static void gaudi_init_hbm_cred(struct hl_device *hdev) (1 << DMA_IF_HBM_CRED_EN_WRITE_CREDIT_EN_SHIFT)); } -static void gaudi_init_rate_limiter(struct hl_device *hdev) -{ - u32 nr, nf, od, sat, rst, timeout; - u64 freq; - - nr = RREG32(mmPSOC_HBM_PLL_NR); - nf = RREG32(mmPSOC_HBM_PLL_NF); - od = RREG32(mmPSOC_HBM_PLL_OD); - freq = (50 * (nf + 1)) / ((nr + 1) * (od + 1)); - - dev_dbg(hdev->dev, "HBM frequency is %lluMHz\n", freq); - - /* Configuration is for five (5) DDMA channels */ - if (freq == 800) { - sat = 4; - rst = 11; - timeout = 15; - } else if (freq == 900) { - sat = 4; - rst = 15; - timeout = 16; - } else if (freq == 950) { - sat = 4; - rst = 15; - timeout = 15; - } else { - dev_warn(hdev->dev, - "unsupported HBM frequency %lluMHz, no rate-limiters\n", - freq); - return; - } - - WREG32(mmDMA_IF_W_S_DOWN_RSP_MID_WGHT_0, 0x111); - WREG32(mmDMA_IF_W_S_DOWN_RSP_MID_WGHT_1, 0x111); - WREG32(mmDMA_IF_E_S_DOWN_RSP_MID_WGHT_0, 0x111); - WREG32(mmDMA_IF_E_S_DOWN_RSP_MID_WGHT_1, 0x111); - WREG32(mmDMA_IF_W_N_DOWN_RSP_MID_WGHT_0, 0x111); - WREG32(mmDMA_IF_W_N_DOWN_RSP_MID_WGHT_1, 0x111); - WREG32(mmDMA_IF_E_N_DOWN_RSP_MID_WGHT_0, 0x111); - WREG32(mmDMA_IF_E_N_DOWN_RSP_MID_WGHT_1, 0x111); - - if (!hdev->rl_enable) { - dev_info(hdev->dev, "Rate limiters disabled\n"); - return; - } - - WREG32(mmDMA_IF_W_S_DOWN_CH0_RL_HBM_SAT, sat); - WREG32(mmDMA_IF_W_S_DOWN_CH1_RL_HBM_SAT, sat); - WREG32(mmDMA_IF_E_S_DOWN_CH0_RL_HBM_SAT, sat); - WREG32(mmDMA_IF_E_S_DOWN_CH1_RL_HBM_SAT, sat); - WREG32(mmDMA_IF_W_N_DOWN_CH0_RL_HBM_SAT, sat); - WREG32(mmDMA_IF_W_N_DOWN_CH1_RL_HBM_SAT, sat); - WREG32(mmDMA_IF_E_N_DOWN_CH0_RL_HBM_SAT, sat); - WREG32(mmDMA_IF_E_N_DOWN_CH1_RL_HBM_SAT, sat); - - WREG32(mmDMA_IF_W_S_DOWN_CH0_RL_HBM_RST, rst); - WREG32(mmDMA_IF_W_S_DOWN_CH1_RL_HBM_RST, rst); - WREG32(mmDMA_IF_E_S_DOWN_CH0_RL_HBM_RST, rst); - WREG32(mmDMA_IF_E_S_DOWN_CH1_RL_HBM_RST, rst); - WREG32(mmDMA_IF_W_N_DOWN_CH0_RL_HBM_RST, rst); - WREG32(mmDMA_IF_W_N_DOWN_CH1_RL_HBM_RST, rst); - WREG32(mmDMA_IF_E_N_DOWN_CH0_RL_HBM_RST, rst); - WREG32(mmDMA_IF_E_N_DOWN_CH1_RL_HBM_RST, rst); - - WREG32(mmDMA_IF_W_S_DOWN_CH0_RL_HBM_TIMEOUT, timeout); - WREG32(mmDMA_IF_W_S_DOWN_CH1_RL_HBM_TIMEOUT, timeout); - WREG32(mmDMA_IF_E_S_DOWN_CH0_RL_HBM_TIMEOUT, timeout); - WREG32(mmDMA_IF_E_S_DOWN_CH1_RL_HBM_TIMEOUT, timeout); - WREG32(mmDMA_IF_W_N_DOWN_CH0_RL_HBM_TIMEOUT, timeout); - WREG32(mmDMA_IF_W_N_DOWN_CH1_RL_HBM_TIMEOUT, timeout); - WREG32(mmDMA_IF_E_N_DOWN_CH0_RL_HBM_TIMEOUT, timeout); - WREG32(mmDMA_IF_E_N_DOWN_CH1_RL_HBM_TIMEOUT, timeout); - - WREG32(mmDMA_IF_W_S_DOWN_CH0_RL_HBM_EN, 1); - WREG32(mmDMA_IF_W_S_DOWN_CH1_RL_HBM_EN, 1); - WREG32(mmDMA_IF_E_S_DOWN_CH0_RL_HBM_EN, 1); - WREG32(mmDMA_IF_E_S_DOWN_CH1_RL_HBM_EN, 1); - WREG32(mmDMA_IF_W_N_DOWN_CH0_RL_HBM_EN, 1); - WREG32(mmDMA_IF_W_N_DOWN_CH1_RL_HBM_EN, 1); - WREG32(mmDMA_IF_E_N_DOWN_CH0_RL_HBM_EN, 1); - WREG32(mmDMA_IF_E_N_DOWN_CH1_RL_HBM_EN, 1); - - WREG32(mmDMA_IF_W_S_DOWN_CH0_RL_SRAM_SAT, sat); - WREG32(mmDMA_IF_W_S_DOWN_CH1_RL_SRAM_SAT, sat); - WREG32(mmDMA_IF_E_S_DOWN_CH0_RL_SRAM_SAT, sat); - WREG32(mmDMA_IF_E_S_DOWN_CH1_RL_SRAM_SAT, sat); - WREG32(mmDMA_IF_W_N_DOWN_CH0_RL_SRAM_SAT, sat); - WREG32(mmDMA_IF_W_N_DOWN_CH1_RL_SRAM_SAT, sat); - WREG32(mmDMA_IF_E_N_DOWN_CH0_RL_SRAM_SAT, sat); - WREG32(mmDMA_IF_E_N_DOWN_CH1_RL_SRAM_SAT, sat); - - WREG32(mmDMA_IF_W_S_DOWN_CH0_RL_SRAM_RST, rst); - WREG32(mmDMA_IF_W_S_DOWN_CH1_RL_SRAM_RST, rst); - WREG32(mmDMA_IF_E_S_DOWN_CH0_RL_SRAM_RST, rst); - WREG32(mmDMA_IF_E_S_DOWN_CH1_RL_SRAM_RST, rst); - WREG32(mmDMA_IF_W_N_DOWN_CH0_RL_SRAM_RST, rst); - WREG32(mmDMA_IF_W_N_DOWN_CH1_RL_SRAM_RST, rst); - WREG32(mmDMA_IF_E_N_DOWN_CH0_RL_SRAM_RST, rst); - WREG32(mmDMA_IF_E_N_DOWN_CH1_RL_SRAM_RST, rst); - - WREG32(mmDMA_IF_W_S_DOWN_CH0_RL_SRAM_TIMEOUT, timeout); - WREG32(mmDMA_IF_W_S_DOWN_CH1_RL_SRAM_TIMEOUT, timeout); - WREG32(mmDMA_IF_E_S_DOWN_CH0_RL_SRAM_TIMEOUT, timeout); - WREG32(mmDMA_IF_E_S_DOWN_CH1_RL_SRAM_TIMEOUT, timeout); - WREG32(mmDMA_IF_W_N_DOWN_CH0_RL_SRAM_TIMEOUT, timeout); - WREG32(mmDMA_IF_W_N_DOWN_CH1_RL_SRAM_TIMEOUT, timeout); - WREG32(mmDMA_IF_E_N_DOWN_CH0_RL_SRAM_TIMEOUT, timeout); - WREG32(mmDMA_IF_E_N_DOWN_CH1_RL_SRAM_TIMEOUT, timeout); - - WREG32(mmDMA_IF_W_S_DOWN_CH0_RL_SRAM_EN, 1); - WREG32(mmDMA_IF_W_S_DOWN_CH1_RL_SRAM_EN, 1); - WREG32(mmDMA_IF_E_S_DOWN_CH0_RL_SRAM_EN, 1); - WREG32(mmDMA_IF_E_S_DOWN_CH1_RL_SRAM_EN, 1); - WREG32(mmDMA_IF_W_N_DOWN_CH0_RL_SRAM_EN, 1); - WREG32(mmDMA_IF_W_N_DOWN_CH1_RL_SRAM_EN, 1); - WREG32(mmDMA_IF_E_N_DOWN_CH0_RL_SRAM_EN, 1); - WREG32(mmDMA_IF_E_N_DOWN_CH1_RL_SRAM_EN, 1); -} - static void gaudi_init_golden_registers(struct hl_device *hdev) { u32 tpc_offset; @@ -1817,8 +1698,6 @@ static void gaudi_init_golden_registers(struct hl_device *hdev) gaudi_init_hbm_cred(hdev); - gaudi_init_rate_limiter(hdev); - gaudi_disable_clock_gating(hdev); for (tpc_id = 0, tpc_offset = 0; @@ -1839,9 +1718,6 @@ static void gaudi_init_golden_registers(struct hl_device *hdev) WREG32(mmMME1_CTRL_EUS_ROLLUP_CNT_ADD, 3); WREG32(mmMME2_CTRL_EUS_ROLLUP_CNT_ADD, 3); WREG32(mmMME3_CTRL_EUS_ROLLUP_CNT_ADD, 3); - - /* WA for H3-2081 */ - WREG32(mmPCIE_WRAP_MAX_OUTSTAND, 0x10ff); } static void gaudi_init_pci_dma_qman(struct hl_device *hdev, int dma_id, diff --git a/drivers/misc/habanalabs/habanalabs_drv.c b/drivers/misc/habanalabs/habanalabs_drv.c index 8652c7e5d7f1..f38664b03865 100644 --- a/drivers/misc/habanalabs/habanalabs_drv.c +++ b/drivers/misc/habanalabs/habanalabs_drv.c @@ -238,7 +238,6 @@ static void set_driver_behavior_per_device(struct hl_device *hdev) hdev->axi_drain = 0; hdev->sram_scrambler_enable = 1; hdev->dram_scrambler_enable = 1; - hdev->rl_enable = 1; hdev->bmc_enable = 1; hdev->hard_reset_on_fw_events = 1; } -- cgit v1.2.3-59-g8ed1b From c16d45f42b64e91895f4bc1cf19febeb5e0c52b6 Mon Sep 17 00:00:00 2001 From: Ofir Bitton Date: Tue, 2 Jun 2020 12:28:27 +0300 Subject: habanalabs: Use pending CS amount per ASIC Training schemes requires much more concurrent command submissions than inference does. In addition, training command submissions can be completed in a non serialized manner. Hence, we add support in which each ASIC will be able to configure the amount of concurrent pending command submissions, rather than use a predefined amount. This change will enhance performance by allowing the user to add more concurrent work without waiting for the previous work to be completed. Signed-off-by: Ofir Bitton Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/command_submission.c | 6 ++++-- drivers/misc/habanalabs/context.c | 14 +++++++++++--- drivers/misc/habanalabs/gaudi/gaudi.c | 2 ++ drivers/misc/habanalabs/gaudi/gaudiP.h | 6 ++++++ drivers/misc/habanalabs/goya/goya.c | 2 ++ drivers/misc/habanalabs/goya/goyaP.h | 6 ++++++ drivers/misc/habanalabs/habanalabs.h | 9 +++++---- drivers/misc/habanalabs/hw_queue.c | 2 +- 8 files changed, 37 insertions(+), 10 deletions(-) diff --git a/drivers/misc/habanalabs/command_submission.c b/drivers/misc/habanalabs/command_submission.c index b0f62cbbdc87..e99c1d126bd3 100644 --- a/drivers/misc/habanalabs/command_submission.c +++ b/drivers/misc/habanalabs/command_submission.c @@ -418,7 +418,8 @@ static int allocate_cs(struct hl_device *hdev, struct hl_ctx *ctx, spin_lock(&ctx->cs_lock); cs_cmpl->cs_seq = ctx->cs_sequence; - other = ctx->cs_pending[cs_cmpl->cs_seq & (HL_MAX_PENDING_CS - 1)]; + other = ctx->cs_pending[cs_cmpl->cs_seq & + (hdev->asic_prop.max_pending_cs - 1)]; if ((other) && (!dma_fence_is_signaled(other))) { spin_unlock(&ctx->cs_lock); dev_dbg(hdev->dev, @@ -432,7 +433,8 @@ static int allocate_cs(struct hl_device *hdev, struct hl_ctx *ctx, cs->sequence = cs_cmpl->cs_seq; - ctx->cs_pending[cs_cmpl->cs_seq & (HL_MAX_PENDING_CS - 1)] = + ctx->cs_pending[cs_cmpl->cs_seq & + (hdev->asic_prop.max_pending_cs - 1)] = &cs_cmpl->base_fence; ctx->cs_sequence++; diff --git a/drivers/misc/habanalabs/context.c b/drivers/misc/habanalabs/context.c index ec92b3506b1f..1b96fefa4a65 100644 --- a/drivers/misc/habanalabs/context.c +++ b/drivers/misc/habanalabs/context.c @@ -22,9 +22,11 @@ static void hl_ctx_fini(struct hl_ctx *ctx) * to this function unless the ref count is 0 */ - for (i = 0 ; i < HL_MAX_PENDING_CS ; i++) + for (i = 0 ; i < hdev->asic_prop.max_pending_cs ; i++) dma_fence_put(ctx->cs_pending[i]); + kfree(ctx->cs_pending); + if (ctx->asid != HL_KERNEL_ASID_ID) { /* The engines are stopped as there is no executing CS, but the * Coresight might be still working by accessing addresses @@ -126,6 +128,11 @@ int hl_ctx_init(struct hl_device *hdev, struct hl_ctx *ctx, bool is_kernel_ctx) spin_lock_init(&ctx->cs_lock); atomic_set(&ctx->thread_ctx_switch_token, 1); ctx->thread_ctx_switch_wait_token = 0; + ctx->cs_pending = kcalloc(hdev->asic_prop.max_pending_cs, + sizeof(struct dma_fence *), + GFP_KERNEL); + if (!ctx->cs_pending) + return -ENOMEM; if (is_kernel_ctx) { ctx->asid = HL_KERNEL_ASID_ID; /* Kernel driver gets ASID 0 */ @@ -170,6 +177,7 @@ int hl_ctx_put(struct hl_ctx *ctx) struct dma_fence *hl_ctx_get_fence(struct hl_ctx *ctx, u64 seq) { + struct asic_fixed_properties *asic_prop = &ctx->hdev->asic_prop; struct dma_fence *fence; spin_lock(&ctx->cs_lock); @@ -179,13 +187,13 @@ struct dma_fence *hl_ctx_get_fence(struct hl_ctx *ctx, u64 seq) return ERR_PTR(-EINVAL); } - if (seq + HL_MAX_PENDING_CS < ctx->cs_sequence) { + if (seq + asic_prop->max_pending_cs < ctx->cs_sequence) { spin_unlock(&ctx->cs_lock); return NULL; } fence = dma_fence_get( - ctx->cs_pending[seq & (HL_MAX_PENDING_CS - 1)]); + ctx->cs_pending[seq & (asic_prop->max_pending_cs - 1)]); spin_unlock(&ctx->cs_lock); return fence; diff --git a/drivers/misc/habanalabs/gaudi/gaudi.c b/drivers/misc/habanalabs/gaudi/gaudi.c index 3ee104b7cb8c..9d9cbcd5a28a 100644 --- a/drivers/misc/habanalabs/gaudi/gaudi.c +++ b/drivers/misc/habanalabs/gaudi/gaudi.c @@ -429,6 +429,8 @@ static int gaudi_get_fixed_properties(struct hl_device *hdev) strncpy(prop->armcp_info.card_name, GAUDI_DEFAULT_CARD_NAME, CARD_NAME_MAX_LEN); + prop->max_pending_cs = GAUDI_MAX_PENDING_CS; + return 0; } diff --git a/drivers/misc/habanalabs/gaudi/gaudiP.h b/drivers/misc/habanalabs/gaudi/gaudiP.h index 41a8d9bff6bf..63baef1e4e99 100644 --- a/drivers/misc/habanalabs/gaudi/gaudiP.h +++ b/drivers/misc/habanalabs/gaudi/gaudiP.h @@ -57,6 +57,12 @@ #define GAUDI_DEFAULT_CARD_NAME "HL2000" +#define GAUDI_MAX_PENDING_CS 1024 + +#if !IS_MAX_PENDING_CS_VALID(GAUDI_MAX_PENDING_CS) +#error "GAUDI_MAX_PENDING_CS must be power of 2 and greater than 1" +#endif + #define PCI_DMA_NUMBER_OF_CHNLS 3 #define HBM_DMA_NUMBER_OF_CHNLS 5 #define DMA_NUMBER_OF_CHNLS (PCI_DMA_NUMBER_OF_CHNLS + \ diff --git a/drivers/misc/habanalabs/goya/goya.c b/drivers/misc/habanalabs/goya/goya.c index a4a20e27ed3b..6dccaec95ffb 100644 --- a/drivers/misc/habanalabs/goya/goya.c +++ b/drivers/misc/habanalabs/goya/goya.c @@ -426,6 +426,8 @@ void goya_get_fixed_properties(struct hl_device *hdev) strncpy(prop->armcp_info.card_name, GOYA_DEFAULT_CARD_NAME, CARD_NAME_MAX_LEN); + + prop->max_pending_cs = GOYA_MAX_PENDING_CS; } /* diff --git a/drivers/misc/habanalabs/goya/goyaP.h b/drivers/misc/habanalabs/goya/goyaP.h index d36f8d90c9c9..9d8a1761252d 100644 --- a/drivers/misc/habanalabs/goya/goyaP.h +++ b/drivers/misc/habanalabs/goya/goyaP.h @@ -57,6 +57,12 @@ #define GOYA_DEFAULT_CARD_NAME "HL1000" +#define GOYA_MAX_PENDING_CS 64 + +#if !IS_MAX_PENDING_CS_VALID(GOYA_MAX_PENDING_CS) +#error "GOYA_MAX_PENDING_CS must be power of 2 and greater than 1" +#endif + /* DRAM Memory Map */ #define CPU_FW_IMAGE_SIZE 0x10000000 /* 256MB */ diff --git a/drivers/misc/habanalabs/habanalabs.h b/drivers/misc/habanalabs/habanalabs.h index 1ecdcf8b763a..64d9b2dd3e19 100644 --- a/drivers/misc/habanalabs/habanalabs.h +++ b/drivers/misc/habanalabs/habanalabs.h @@ -42,9 +42,6 @@ #define HL_MAX_QUEUES 128 -/* MUST BE POWER OF 2 and larger than 1 */ -#define HL_MAX_PENDING_CS 64 - #define HL_IDLE_BUSY_TS_ARR_SIZE 4096 /* Memory */ @@ -61,6 +58,9 @@ #define HL_MAX_SOB_VAL (1 << 15) +#define IS_POWER_OF_2(n) (n != 0 && ((n & (n - 1)) == 0)) +#define IS_MAX_PENDING_CS_VALID(n) (IS_POWER_OF_2(n) && (n > 1)) + /** * struct pgt_info - MMU hop page info. * @node: hash linked-list node for the pgts shadow hash of pgts. @@ -285,6 +285,7 @@ struct asic_fixed_properties { u32 high_pll; u32 cb_pool_cb_cnt; u32 cb_pool_cb_size; + u32 max_pending_cs; u8 tpc_enabled_mask; u8 completion_queues_count; }; @@ -782,7 +783,7 @@ struct hl_ctx { struct hl_fpriv *hpriv; struct hl_device *hdev; struct kref refcount; - struct dma_fence *cs_pending[HL_MAX_PENDING_CS]; + struct dma_fence **cs_pending; struct hl_va_range *host_va_range; struct hl_va_range *host_huge_va_range; struct hl_va_range *dram_va_range; diff --git a/drivers/misc/habanalabs/hw_queue.c b/drivers/misc/habanalabs/hw_queue.c index f4434b39ef1b..29b96d24edc2 100644 --- a/drivers/misc/habanalabs/hw_queue.c +++ b/drivers/misc/habanalabs/hw_queue.c @@ -376,7 +376,7 @@ static void hw_queue_schedule_job(struct hl_cs_job *job) * write address offset in the SM block (QMAN LBW message). * The write address offset is calculated as "COMP_OFFSET << 2". */ - offset = job->cs->sequence & (HL_MAX_PENDING_CS - 1); + offset = job->cs->sequence & (hdev->asic_prop.max_pending_cs - 1); ctl = ((offset << BD_CTL_COMP_OFFSET_SHIFT) & BD_CTL_COMP_OFFSET_MASK) | ((q->pi << BD_CTL_COMP_DATA_SHIFT) & BD_CTL_COMP_DATA_MASK); -- cgit v1.2.3-59-g8ed1b From 21e7a34634495939b195e5dea51e7628c3d7ab3a Mon Sep 17 00:00:00 2001 From: Ofir Bitton Date: Thu, 14 May 2020 18:25:47 +0300 Subject: habanalabs: sync stream generic functionality Currently sync stream is limited only for external queues. We want to remove this constraint by adding a new queue property dedicated for sync stream. In addition we move the initialization and reset methods to the common code since we can re-use them with slight changes. Signed-off-by: Ofir Bitton Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/command_submission.c | 6 ++-- drivers/misc/habanalabs/gaudi/gaudi.c | 46 +++----------------------- drivers/misc/habanalabs/gaudi/gaudiP.h | 2 -- drivers/misc/habanalabs/goya/goya.c | 12 ------- drivers/misc/habanalabs/habanalabs.h | 19 ++++++++--- drivers/misc/habanalabs/hw_queue.c | 48 +++++++++++++++++++++++++--- 6 files changed, 67 insertions(+), 66 deletions(-) diff --git a/drivers/misc/habanalabs/command_submission.c b/drivers/misc/habanalabs/command_submission.c index e99c1d126bd3..62dab99dda98 100644 --- a/drivers/misc/habanalabs/command_submission.c +++ b/drivers/misc/habanalabs/command_submission.c @@ -740,6 +740,7 @@ static int cs_ioctl_signal_wait(struct hl_fpriv *hpriv, enum hl_cs_type cs_type, struct hl_cs_job *job; struct hl_cs *cs; struct hl_cb *cb; + enum hl_queue_type q_type; u64 *signal_seq_arr = NULL, signal_seq; u32 size_to_copy, q_idx, signal_seq_arr_len, cb_size; int rc; @@ -772,9 +773,10 @@ static int cs_ioctl_signal_wait(struct hl_fpriv *hpriv, enum hl_cs_type cs_type, chunk = &cs_chunk_array[0]; q_idx = chunk->queue_index; hw_queue_prop = &hdev->asic_prop.hw_queues_props[q_idx]; + q_type = hw_queue_prop->type; if ((q_idx >= HL_MAX_QUEUES) || - (hw_queue_prop->type != QUEUE_TYPE_EXT)) { + (!hw_queue_prop->supports_sync_stream)) { dev_err(hdev->dev, "Queue index %d is invalid\n", q_idx); rc = -EINVAL; goto free_cs_chunk_array; @@ -871,7 +873,7 @@ static int cs_ioctl_signal_wait(struct hl_fpriv *hpriv, enum hl_cs_type cs_type, *cs_seq = cs->sequence; - job = hl_cs_allocate_job(hdev, QUEUE_TYPE_EXT, true); + job = hl_cs_allocate_job(hdev, q_type, true); if (!job) { dev_err(hdev->dev, "Failed to allocate a new job\n"); rc = -ENOMEM; diff --git a/drivers/misc/habanalabs/gaudi/gaudi.c b/drivers/misc/habanalabs/gaudi/gaudi.c index 9d9cbcd5a28a..fc377c618af0 100644 --- a/drivers/misc/habanalabs/gaudi/gaudi.c +++ b/drivers/misc/habanalabs/gaudi/gaudi.c @@ -345,10 +345,12 @@ static int gaudi_get_fixed_properties(struct hl_device *hdev) prop->hw_queues_props[i].type = QUEUE_TYPE_EXT; prop->hw_queues_props[i].driver_only = 0; prop->hw_queues_props[i].requires_kernel_cb = 1; + prop->hw_queues_props[i].supports_sync_stream = 1; } else if (gaudi_queue_type[i] == QUEUE_TYPE_CPU) { prop->hw_queues_props[i].type = QUEUE_TYPE_CPU; prop->hw_queues_props[i].driver_only = 1; prop->hw_queues_props[i].requires_kernel_cb = 0; + prop->hw_queues_props[i].supports_sync_stream = 0; } else if (gaudi_queue_type[i] == QUEUE_TYPE_INT) { prop->hw_queues_props[i].type = QUEUE_TYPE_INT; prop->hw_queues_props[i].driver_only = 0; @@ -357,6 +359,7 @@ static int gaudi_get_fixed_properties(struct hl_device *hdev) prop->hw_queues_props[i].type = QUEUE_TYPE_NA; prop->hw_queues_props[i].driver_only = 0; prop->hw_queues_props[i].requires_kernel_cb = 0; + prop->hw_queues_props[i].supports_sync_stream = 0; } } @@ -364,7 +367,8 @@ static int gaudi_get_fixed_properties(struct hl_device *hdev) prop->hw_queues_props[i].type = QUEUE_TYPE_NA; prop->completion_queues_count = NUMBER_OF_CMPLT_QUEUES; - + prop->sync_stream_first_sob = 0; + prop->sync_stream_first_mon = 0; prop->dram_base_address = DRAM_PHYS_BASE; prop->dram_size = GAUDI_HBM_SIZE_32GB; prop->dram_end_address = prop->dram_base_address + @@ -6296,44 +6300,6 @@ static u32 gaudi_get_queue_id_for_cq(struct hl_device *hdev, u32 cq_idx) return gaudi_cq_assignment[cq_idx]; } -static void gaudi_ext_queue_init(struct hl_device *hdev, u32 q_idx) -{ - struct gaudi_device *gaudi = hdev->asic_specific; - struct hl_hw_queue *hw_queue = &hdev->kernel_queues[q_idx]; - struct hl_hw_sob *hw_sob; - int sob, ext_idx = gaudi->ext_queue_idx++; - - /* - * The external queues might not sit sequentially, hence use the - * real external queue index for the SOB/MON base id. - */ - hw_queue->base_sob_id = ext_idx * HL_RSVD_SOBS; - hw_queue->base_mon_id = ext_idx * HL_RSVD_MONS; - hw_queue->next_sob_val = 1; - hw_queue->curr_sob_offset = 0; - - for (sob = 0 ; sob < HL_RSVD_SOBS ; sob++) { - hw_sob = &hw_queue->hw_sob[sob]; - hw_sob->hdev = hdev; - hw_sob->sob_id = hw_queue->base_sob_id + sob; - hw_sob->q_idx = q_idx; - kref_init(&hw_sob->kref); - } -} - -static void gaudi_ext_queue_reset(struct hl_device *hdev, u32 q_idx) -{ - struct hl_hw_queue *hw_queue = &hdev->kernel_queues[q_idx]; - - /* - * In case we got here due to a stuck CS, the refcnt might be bigger - * than 1 and therefore we reset it. - */ - kref_init(&hw_queue->hw_sob[hw_queue->curr_sob_offset].kref); - hw_queue->curr_sob_offset = 0; - hw_queue->next_sob_val = 1; -} - static u32 gaudi_get_signal_cb_size(struct hl_device *hdev) { return sizeof(struct packet_msg_short) + @@ -6636,8 +6602,6 @@ static const struct hl_asic_funcs gaudi_funcs = { .read_device_fw_version = gaudi_read_device_fw_version, .load_firmware_to_device = gaudi_load_firmware_to_device, .load_boot_fit_to_device = gaudi_load_boot_fit_to_device, - .ext_queue_init = gaudi_ext_queue_init, - .ext_queue_reset = gaudi_ext_queue_reset, .get_signal_cb_size = gaudi_get_signal_cb_size, .get_wait_cb_size = gaudi_get_wait_cb_size, .gen_signal_cb = gaudi_gen_signal_cb, diff --git a/drivers/misc/habanalabs/gaudi/gaudiP.h b/drivers/misc/habanalabs/gaudi/gaudiP.h index 63baef1e4e99..3958fe38c8ee 100644 --- a/drivers/misc/habanalabs/gaudi/gaudiP.h +++ b/drivers/misc/habanalabs/gaudi/gaudiP.h @@ -234,7 +234,6 @@ struct gaudi_internal_qman_info { * engine. * @multi_msi_mode: whether we are working in multi MSI single MSI mode. * Multi MSI is possible only with IOMMU enabled. - * @ext_queue_idx: helper index for external queues initialization. * @mmu_cache_inv_pi: PI for MMU cache invalidation flow. The H/W expects an * 8-bit value so use u8. */ @@ -255,7 +254,6 @@ struct gaudi_device { u32 events_stat_aggregate[GAUDI_EVENT_SIZE]; u32 hw_cap_initialized; u8 multi_msi_mode; - u8 ext_queue_idx; u8 mmu_cache_inv_pi; }; diff --git a/drivers/misc/habanalabs/goya/goya.c b/drivers/misc/habanalabs/goya/goya.c index 6dccaec95ffb..ff9e8a31ced4 100644 --- a/drivers/misc/habanalabs/goya/goya.c +++ b/drivers/misc/habanalabs/goya/goya.c @@ -5156,16 +5156,6 @@ u32 goya_get_queue_id_for_cq(struct hl_device *hdev, u32 cq_idx) return cq_idx; } -static void goya_ext_queue_init(struct hl_device *hdev, u32 q_idx) -{ - -} - -static void goya_ext_queue_reset(struct hl_device *hdev, u32 q_idx) -{ - -} - static u32 goya_get_signal_cb_size(struct hl_device *hdev) { return 0; @@ -5279,8 +5269,6 @@ static const struct hl_asic_funcs goya_funcs = { .read_device_fw_version = goya_read_device_fw_version, .load_firmware_to_device = goya_load_firmware_to_device, .load_boot_fit_to_device = goya_load_boot_fit_to_device, - .ext_queue_init = goya_ext_queue_init, - .ext_queue_reset = goya_ext_queue_reset, .get_signal_cb_size = goya_get_signal_cb_size, .get_wait_cb_size = goya_get_wait_cb_size, .gen_signal_cb = goya_gen_signal_cb, diff --git a/drivers/misc/habanalabs/habanalabs.h b/drivers/misc/habanalabs/habanalabs.h index 64d9b2dd3e19..8cd4b55d0608 100644 --- a/drivers/misc/habanalabs/habanalabs.h +++ b/drivers/misc/habanalabs/habanalabs.h @@ -50,6 +50,10 @@ /* MMU */ #define MMU_HASH_TABLE_BITS 7 /* 1 << 7 buckets */ +/* + * HL_RSVD_SOBS 'sync stream' reserved sync objects per QMAN stream + * HL_RSVD_MONS 'sync stream' reserved monitors per QMAN stream + */ #define HL_RSVD_SOBS 4 #define HL_RSVD_MONS 2 @@ -141,11 +145,13 @@ struct hl_hw_sob { * false otherwise. * @requires_kernel_cb: true if a CB handle must be provided for jobs on this * queue, false otherwise (a CB address must be provided). + * @supports_sync_stream: True if queue supports sync stream */ struct hw_queue_properties { enum hl_queue_type type; u8 driver_only; u8 requires_kernel_cb; + u8 supports_sync_stream; }; /** @@ -245,6 +251,9 @@ struct hl_mmu_properties { * @cb_pool_cb_cnt: number of CBs in the CB pool. * @cb_pool_cb_size: size of each CB in the CB pool. * @tpc_enabled_mask: which TPCs are enabled. + * @sync_stream_first_sob: first sync object available for sync stream use + * @sync_stream_first_mon: first monitor available for sync stream use + * @tpc_enabled_mask: which TPCs are enabled. * @completion_queues_count: number of completion queues. */ struct asic_fixed_properties { @@ -286,6 +295,8 @@ struct asic_fixed_properties { u32 cb_pool_cb_cnt; u32 cb_pool_cb_size; u32 max_pending_cs; + u16 sync_stream_first_sob; + u16 sync_stream_first_mon; u8 tpc_enabled_mask; u8 completion_queues_count; }; @@ -423,6 +434,7 @@ struct hl_cs_job; * exist). * @curr_sob_offset: the id offset to the currently used SOB from the * HL_RSVD_SOBS that are being used by this queue. + * @supports_sync_stream: True if queue supports sync stream */ struct hl_hw_queue { struct hl_hw_sob hw_sob[HL_RSVD_SOBS]; @@ -441,6 +453,7 @@ struct hl_hw_queue { u16 base_mon_id; u8 valid; u8 curr_sob_offset; + u8 supports_sync_stream; }; /** @@ -603,8 +616,6 @@ enum hl_pll_frequency { * contained in registers * @load_firmware_to_device: load the firmware to the device's memory * @load_boot_fit_to_device: load boot fit to device's memory - * @ext_queue_init: Initialize the given external queue. - * @ext_queue_reset: Reset the given external queue. * @get_signal_cb_size: Get signal CB size. * @get_wait_cb_size: Get wait CB size. * @gen_signal_cb: Generate a signal CB. @@ -707,8 +718,6 @@ struct hl_asic_funcs { enum hl_fw_component fwc); int (*load_firmware_to_device)(struct hl_device *hdev); int (*load_boot_fit_to_device)(struct hl_device *hdev); - void (*ext_queue_init)(struct hl_device *hdev, u32 hw_queue_id); - void (*ext_queue_reset)(struct hl_device *hdev, u32 hw_queue_id); u32 (*get_signal_cb_size)(struct hl_device *hdev); u32 (*get_wait_cb_size)(struct hl_device *hdev); void (*gen_signal_cb)(struct hl_device *hdev, void *data, u16 sob_id); @@ -1436,6 +1445,7 @@ struct hl_device_idle_busy_ts { * @cdev_sysfs_created: were char devices and sysfs nodes created. * @stop_on_err: true if engines should stop on error. * @supports_sync_stream: is sync stream supported. + * @sync_stream_queue_idx: helper index for sync stream queues initialization. * @supports_coresight: is CoreSight supported. * @supports_soft_reset: is soft reset supported. */ @@ -1523,6 +1533,7 @@ struct hl_device { u8 cdev_sysfs_created; u8 stop_on_err; u8 supports_sync_stream; + u8 sync_stream_queue_idx; u8 supports_coresight; u8 supports_soft_reset; diff --git a/drivers/misc/habanalabs/hw_queue.c b/drivers/misc/habanalabs/hw_queue.c index 29b96d24edc2..27f0c34b63b9 100644 --- a/drivers/misc/habanalabs/hw_queue.c +++ b/drivers/misc/habanalabs/hw_queue.c @@ -663,9 +663,6 @@ static int ext_and_cpu_queue_init(struct hl_device *hdev, struct hl_hw_queue *q, q->ci = 0; q->pi = 0; - if (!is_cpu_queue) - hdev->asic_funcs->ext_queue_init(hdev, q->hw_queue_id); - return 0; free_queue: @@ -732,6 +729,42 @@ static int hw_queue_init(struct hl_device *hdev, struct hl_hw_queue *q) return 0; } +static void sync_stream_queue_init(struct hl_device *hdev, u32 q_idx) +{ + struct hl_hw_queue *hw_queue = &hdev->kernel_queues[q_idx]; + struct asic_fixed_properties *prop = &hdev->asic_prop; + struct hl_hw_sob *hw_sob; + int sob, queue_idx = hdev->sync_stream_queue_idx++; + + hw_queue->base_sob_id = + prop->sync_stream_first_sob + queue_idx * HL_RSVD_SOBS; + hw_queue->base_mon_id = + prop->sync_stream_first_mon + queue_idx * HL_RSVD_MONS; + hw_queue->next_sob_val = 1; + hw_queue->curr_sob_offset = 0; + + for (sob = 0 ; sob < HL_RSVD_SOBS ; sob++) { + hw_sob = &hw_queue->hw_sob[sob]; + hw_sob->hdev = hdev; + hw_sob->sob_id = hw_queue->base_sob_id + sob; + hw_sob->q_idx = q_idx; + kref_init(&hw_sob->kref); + } +} + +static void sync_stream_queue_reset(struct hl_device *hdev, u32 q_idx) +{ + struct hl_hw_queue *hw_queue = &hdev->kernel_queues[q_idx]; + + /* + * In case we got here due to a stuck CS, the refcnt might be bigger + * than 1 and therefore we reset it. + */ + kref_init(&hw_queue->hw_sob[hw_queue->curr_sob_offset].kref); + hw_queue->curr_sob_offset = 0; + hw_queue->next_sob_val = 1; +} + /* * queue_init - main initialization function for H/W queue object * @@ -774,6 +807,9 @@ static int queue_init(struct hl_device *hdev, struct hl_hw_queue *q, break; } + if (q->supports_sync_stream) + sync_stream_queue_init(hdev, q->hw_queue_id); + if (rc) return rc; @@ -848,6 +884,8 @@ int hl_hw_queues_create(struct hl_device *hdev) i < HL_MAX_QUEUES ; i++, q_ready_cnt++, q++) { q->queue_type = asic->hw_queues_props[i].type; + q->supports_sync_stream = + asic->hw_queues_props[i].supports_sync_stream; rc = queue_init(hdev, q, i); if (rc) { dev_err(hdev->dev, @@ -889,7 +927,7 @@ void hl_hw_queue_reset(struct hl_device *hdev, bool hard_reset) continue; q->pi = q->ci = 0; - if (q->queue_type == QUEUE_TYPE_EXT) - hdev->asic_funcs->ext_queue_reset(hdev, q->hw_queue_id); + if (q->supports_sync_stream) + sync_stream_queue_reset(hdev, q->hw_queue_id); } } -- cgit v1.2.3-59-g8ed1b From 6c07bab34b07eee62cfdc4a0bcce2732986b4288 Mon Sep 17 00:00:00 2001 From: Ofir Bitton Date: Mon, 1 Jun 2020 10:38:46 +0300 Subject: habanalabs: Use mask instead of shift in sync stream registers Use proper bitfield masks instead of shifting values when configuring packets sent to device. Signed-off-by: Ofir Bitton Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/gaudi/gaudi.c | 70 +++++++++++----------- .../misc/habanalabs/include/gaudi/gaudi_packets.h | 4 +- 2 files changed, 38 insertions(+), 36 deletions(-) diff --git a/drivers/misc/habanalabs/gaudi/gaudi.c b/drivers/misc/habanalabs/gaudi/gaudi.c index fc377c618af0..0c5d5831f327 100644 --- a/drivers/misc/habanalabs/gaudi/gaudi.c +++ b/drivers/misc/habanalabs/gaudi/gaudi.c @@ -21,6 +21,7 @@ #include #include #include +#include /* * Gaudi security scheme: @@ -6322,16 +6323,17 @@ static void gaudi_gen_signal_cb(struct hl_device *hdev, void *data, u16 sob_id) pkt = (struct packet_msg_short *) (uintptr_t) cb->kernel_address; memset(pkt, 0, sizeof(*pkt)); - value = 1 << GAUDI_PKT_SHORT_VAL_SOB_SYNC_VAL_SHIFT; /* inc by 1 */ - value |= 1 << GAUDI_PKT_SHORT_VAL_SOB_MOD_SHIFT; /* add mode */ + /* Inc by 1, Mode ADD */ + value = FIELD_PREP(GAUDI_PKT_SHORT_VAL_SOB_SYNC_VAL_MASK, 1); + value |= FIELD_PREP(GAUDI_PKT_SHORT_VAL_SOB_MOD_MASK, 1); - ctl = (sob_id * 4) << GAUDI_PKT_SHORT_CTL_ADDR_SHIFT; /* SOB id */ - ctl |= 0 << GAUDI_PKT_SHORT_CTL_OP_SHIFT; /* write the value */ - ctl |= 3 << GAUDI_PKT_SHORT_CTL_BASE_SHIFT; /* W_S SOB base */ - ctl |= PACKET_MSG_SHORT << GAUDI_PKT_SHORT_CTL_OPCODE_SHIFT; - ctl |= 1 << GAUDI_PKT_SHORT_CTL_EB_SHIFT; - ctl |= 1 << GAUDI_PKT_SHORT_CTL_RB_SHIFT; - ctl |= 1 << GAUDI_PKT_SHORT_CTL_MB_SHIFT; + ctl = FIELD_PREP(GAUDI_PKT_SHORT_CTL_ADDR_MASK, sob_id * 4); + ctl |= FIELD_PREP(GAUDI_PKT_SHORT_CTL_OP_MASK, 0); /* write the value */ + ctl |= FIELD_PREP(GAUDI_PKT_SHORT_CTL_BASE_MASK, 3); /* W_S SOB base */ + ctl |= FIELD_PREP(GAUDI_PKT_SHORT_CTL_OPCODE_MASK, PACKET_MSG_SHORT); + ctl |= FIELD_PREP(GAUDI_PKT_SHORT_CTL_EB_MASK, 1); + ctl |= FIELD_PREP(GAUDI_PKT_SHORT_CTL_RB_MASK, 1); + ctl |= FIELD_PREP(GAUDI_PKT_SHORT_CTL_MB_MASK, 1); pkt->value = cpu_to_le32(value); pkt->ctl = cpu_to_le32(ctl); @@ -6344,12 +6346,12 @@ static u32 gaudi_add_mon_msg_short(struct packet_msg_short *pkt, u32 value, memset(pkt, 0, pkt_size); - ctl = addr << GAUDI_PKT_SHORT_CTL_ADDR_SHIFT; - ctl |= 2 << GAUDI_PKT_SHORT_CTL_BASE_SHIFT; /* W_S MON base */ - ctl |= PACKET_MSG_SHORT << GAUDI_PKT_SHORT_CTL_OPCODE_SHIFT; - ctl |= 0 << GAUDI_PKT_SHORT_CTL_EB_SHIFT; - ctl |= 1 << GAUDI_PKT_SHORT_CTL_RB_SHIFT; - ctl |= 0 << GAUDI_PKT_SHORT_CTL_MB_SHIFT; /* only last pkt needs MB */ + ctl = FIELD_PREP(GAUDI_PKT_SHORT_CTL_ADDR_MASK, addr); + ctl |= FIELD_PREP(GAUDI_PKT_SHORT_CTL_BASE_MASK, 2); /* W_S MON base */ + ctl |= FIELD_PREP(GAUDI_PKT_SHORT_CTL_OPCODE_MASK, PACKET_MSG_SHORT); + ctl |= FIELD_PREP(GAUDI_PKT_SHORT_CTL_EB_MASK, 0); + ctl |= FIELD_PREP(GAUDI_PKT_SHORT_CTL_RB_MASK, 1); + ctl |= FIELD_PREP(GAUDI_PKT_SHORT_CTL_MB_MASK, 0); /* last pkt MB */ pkt->value = cpu_to_le32(value); pkt->ctl = cpu_to_le32(ctl); @@ -6365,18 +6367,19 @@ static u32 gaudi_add_arm_monitor_pkt(struct packet_msg_short *pkt, u16 sob_id, memset(pkt, 0, pkt_size); - value = (sob_id / 8) << GAUDI_PKT_SHORT_VAL_MON_SYNC_GID_SHIFT; - value |= sob_val << GAUDI_PKT_SHORT_VAL_MON_SYNC_VAL_SHIFT; - value |= 0 << GAUDI_PKT_SHORT_VAL_MON_MODE_SHIFT; /* GREATER_OR_EQUAL */ - value |= mask << GAUDI_PKT_SHORT_VAL_MON_MASK_SHIFT; + value = FIELD_PREP(GAUDI_PKT_SHORT_VAL_MON_SYNC_GID_MASK, sob_id / 8); + value |= FIELD_PREP(GAUDI_PKT_SHORT_VAL_MON_SYNC_VAL_MASK, sob_val); + value |= FIELD_PREP(GAUDI_PKT_SHORT_VAL_MON_MODE_MASK, + 0); /* GREATER OR EQUAL*/ + value |= FIELD_PREP(GAUDI_PKT_SHORT_VAL_MON_MASK_MASK, mask); - ctl = addr << GAUDI_PKT_SHORT_CTL_ADDR_SHIFT; - ctl |= 0 << GAUDI_PKT_SHORT_CTL_OP_SHIFT; /* write the value */ - ctl |= 2 << GAUDI_PKT_SHORT_CTL_BASE_SHIFT; /* W_S MON base */ - ctl |= PACKET_MSG_SHORT << GAUDI_PKT_SHORT_CTL_OPCODE_SHIFT; - ctl |= 0 << GAUDI_PKT_SHORT_CTL_EB_SHIFT; - ctl |= 1 << GAUDI_PKT_SHORT_CTL_RB_SHIFT; - ctl |= 1 << GAUDI_PKT_SHORT_CTL_MB_SHIFT; + ctl = FIELD_PREP(GAUDI_PKT_SHORT_CTL_ADDR_MASK, addr); + ctl |= FIELD_PREP(GAUDI_PKT_SHORT_CTL_OP_MASK, 0); /* write the value */ + ctl |= FIELD_PREP(GAUDI_PKT_SHORT_CTL_BASE_MASK, 2); /* W_S MON base */ + ctl |= FIELD_PREP(GAUDI_PKT_SHORT_CTL_OPCODE_MASK, PACKET_MSG_SHORT); + ctl |= FIELD_PREP(GAUDI_PKT_SHORT_CTL_EB_MASK, 0); + ctl |= FIELD_PREP(GAUDI_PKT_SHORT_CTL_RB_MASK, 1); + ctl |= FIELD_PREP(GAUDI_PKT_SHORT_CTL_MB_MASK, 1); pkt->value = cpu_to_le32(value); pkt->ctl = cpu_to_le32(ctl); @@ -6390,15 +6393,14 @@ static u32 gaudi_add_fence_pkt(struct packet_fence *pkt) memset(pkt, 0, pkt_size); - cfg = 1 << GAUDI_PKT_FENCE_CFG_DEC_VAL_SHIFT; - cfg |= 1 << GAUDI_PKT_FENCE_CFG_TARGET_VAL_SHIFT; - cfg |= 2 << GAUDI_PKT_FENCE_CFG_ID_SHIFT; + cfg = FIELD_PREP(GAUDI_PKT_FENCE_CFG_DEC_VAL_MASK, 1); + cfg |= FIELD_PREP(GAUDI_PKT_FENCE_CFG_TARGET_VAL_MASK, 1); + cfg |= FIELD_PREP(GAUDI_PKT_FENCE_CFG_ID_MASK, 2); - ctl = 0 << GAUDI_PKT_FENCE_CTL_PRED_SHIFT; - ctl |= PACKET_FENCE << GAUDI_PKT_FENCE_CTL_OPCODE_SHIFT; - ctl |= 0 << GAUDI_PKT_FENCE_CTL_EB_SHIFT; - ctl |= 1 << GAUDI_PKT_FENCE_CTL_RB_SHIFT; - ctl |= 1 << GAUDI_PKT_FENCE_CTL_MB_SHIFT; + ctl = FIELD_PREP(GAUDI_PKT_FENCE_CTL_OPCODE_MASK, PACKET_FENCE); + ctl |= FIELD_PREP(GAUDI_PKT_SHORT_CTL_EB_MASK, 0); + ctl |= FIELD_PREP(GAUDI_PKT_SHORT_CTL_RB_MASK, 1); + ctl |= FIELD_PREP(GAUDI_PKT_SHORT_CTL_MB_MASK, 1); pkt->cfg = cpu_to_le32(cfg); pkt->ctl = cpu_to_le32(ctl); diff --git a/drivers/misc/habanalabs/include/gaudi/gaudi_packets.h b/drivers/misc/habanalabs/include/gaudi/gaudi_packets.h index 0f0cd067bb43..f30f2c0458d7 100644 --- a/drivers/misc/habanalabs/include/gaudi/gaudi_packets.h +++ b/drivers/misc/habanalabs/include/gaudi/gaudi_packets.h @@ -85,7 +85,7 @@ struct packet_msg_long { }; #define GAUDI_PKT_SHORT_VAL_SOB_SYNC_VAL_SHIFT 0 -#define GAUDI_PKT_SHORT_VAL_SOB_SYNC_VAL_MASK 0x0000EFFF +#define GAUDI_PKT_SHORT_VAL_SOB_SYNC_VAL_MASK 0x00007FFF #define GAUDI_PKT_SHORT_VAL_SOB_MOD_SHIFT 31 #define GAUDI_PKT_SHORT_VAL_SOB_MOD_MASK 0x80000000 @@ -141,7 +141,7 @@ struct packet_msg_prot { #define GAUDI_PKT_FENCE_CFG_TARGET_VAL_MASK 0x00FF0000 #define GAUDI_PKT_FENCE_CFG_ID_SHIFT 30 -#define GAUDI_PKT_FENCE_CFG_ID_MASK 0xC000000 +#define GAUDI_PKT_FENCE_CFG_ID_MASK 0xC0000000 #define GAUDI_PKT_FENCE_CTL_PRED_SHIFT 0 #define GAUDI_PKT_FENCE_CTL_PRED_MASK 0x0000001F -- cgit v1.2.3-59-g8ed1b From 3bf1c021e36e7269c71dceafec713b0181f413a9 Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Tue, 9 Jun 2020 16:14:55 +0300 Subject: uapi/habanalabs: fix some comments MAP/UNMAP are done also for device memory. Reviewed-by: Omer Shpigelman Signed-off-by: Oded Gabbay --- include/uapi/misc/habanalabs.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/uapi/misc/habanalabs.h b/include/uapi/misc/habanalabs.h index f6267a8d7416..f218d1c62c62 100644 --- a/include/uapi/misc/habanalabs.h +++ b/include/uapi/misc/habanalabs.h @@ -530,13 +530,13 @@ union hl_wait_cs_args { struct hl_wait_cs_out out; }; -/* Opcode to alloc device memory */ +/* Opcode to allocate device memory */ #define HL_MEM_OP_ALLOC 0 /* Opcode to free previously allocated device memory */ #define HL_MEM_OP_FREE 1 -/* Opcode to map host memory */ +/* Opcode to map host and device memory */ #define HL_MEM_OP_MAP 2 -/* Opcode to unmap previously mapped host memory */ +/* Opcode to unmap previously mapped host and device memory */ #define HL_MEM_OP_UNMAP 3 /* Memory flags */ -- cgit v1.2.3-59-g8ed1b From 6ced91170df88ff03b7d4c9ec87c185ebf8443be Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Tue, 9 Jun 2020 19:58:44 +0300 Subject: habanalabs: align armcp_packet structure to 8 bytes Once there is a 64-bit field in a structure, GCC compiler for ARM aligns the structure to 8 bytes. In order to avoid confusion when these structures are being passed between CPUs from different architectures, we explicitly align the structure to 8 bytes. Reviewed-by: Omer Shpigelman Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/include/armcp_if.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/misc/habanalabs/include/armcp_if.h b/drivers/misc/habanalabs/include/armcp_if.h index a34fc39ad87e..dea7c90faafa 100644 --- a/drivers/misc/habanalabs/include/armcp_if.h +++ b/drivers/misc/habanalabs/include/armcp_if.h @@ -276,6 +276,8 @@ struct armcp_packet { /* For get Armcp info/EEPROM data */ __le32 data_max_size; }; + + __le32 reserved; }; struct armcp_unmask_irq_arr_packet { -- cgit v1.2.3-59-g8ed1b From e8edded6939e4c194ab302d4913cb1a9319561d9 Mon Sep 17 00:00:00 2001 From: Adam Aharon Date: Tue, 26 May 2020 11:04:30 +0300 Subject: habanalabs: calculate trace frequency from PLL The profiler needs to know the PLL values for correctly showing the profiling data. Because our firmware can use different PLL configurations, we need to read the PLL values from the ASIC to pass them to the profiler. Signed-off-by: Adam Aharon Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/gaudi/gaudi.c | 33 +++++- drivers/misc/habanalabs/gaudi/gaudi_coresight.c | 6 +- drivers/misc/habanalabs/goya/goya.c | 33 +++++- drivers/misc/habanalabs/goya/goya_coresight.c | 6 +- drivers/misc/habanalabs/habanalabs.h | 11 ++ .../habanalabs/include/gaudi/asic_reg/gaudi_regs.h | 1 + .../include/gaudi/asic_reg/psoc_cpu_pll_regs.h | 114 +++++++++++++++++++++ 7 files changed, 194 insertions(+), 10 deletions(-) create mode 100644 drivers/misc/habanalabs/include/gaudi/asic_reg/psoc_cpu_pll_regs.h diff --git a/drivers/misc/habanalabs/gaudi/gaudi.c b/drivers/misc/habanalabs/gaudi/gaudi.c index 0c5d5831f327..aa4139626a04 100644 --- a/drivers/misc/habanalabs/gaudi/gaudi.c +++ b/drivers/misc/habanalabs/gaudi/gaudi.c @@ -555,11 +555,36 @@ static int gaudi_early_fini(struct hl_device *hdev) static void gaudi_fetch_psoc_frequency(struct hl_device *hdev) { struct asic_fixed_properties *prop = &hdev->asic_prop; + u32 trace_freq = 0; + u32 pll_clk = 0; + u32 div_fctr = RREG32(mmPSOC_CPU_PLL_DIV_FACTOR_2); + u32 div_sel = RREG32(mmPSOC_CPU_PLL_DIV_SEL_2); + u32 nr = RREG32(mmPSOC_CPU_PLL_NR); + u32 nf = RREG32(mmPSOC_CPU_PLL_NF); + u32 od = RREG32(mmPSOC_CPU_PLL_OD); + + if (div_sel == DIV_SEL_REF_CLK || div_sel == DIV_SEL_DIVIDED_REF) { + if (div_sel == DIV_SEL_REF_CLK) + trace_freq = PLL_REF_CLK; + else + trace_freq = PLL_REF_CLK / (div_fctr + 1); + } else if (div_sel == DIV_SEL_PLL_CLK || + div_sel == DIV_SEL_DIVIDED_PLL) { + pll_clk = PLL_REF_CLK * (nf + 1) / ((nr + 1) * (od + 1)); + if (div_sel == DIV_SEL_PLL_CLK) + trace_freq = pll_clk; + else + trace_freq = pll_clk / (div_fctr + 1); + } else { + dev_warn(hdev->dev, + "Received invalid div select value: %d", div_sel); + } - prop->psoc_pci_pll_nr = RREG32(mmPSOC_PCI_PLL_NR); - prop->psoc_pci_pll_nf = RREG32(mmPSOC_PCI_PLL_NF); - prop->psoc_pci_pll_od = RREG32(mmPSOC_PCI_PLL_OD); - prop->psoc_pci_pll_div_factor = RREG32(mmPSOC_PCI_PLL_DIV_FACTOR_1); + prop->psoc_timestamp_frequency = trace_freq; + prop->psoc_pci_pll_nr = nr; + prop->psoc_pci_pll_nf = nf; + prop->psoc_pci_pll_od = od; + prop->psoc_pci_pll_div_factor = div_fctr; } static int _gaudi_init_tpc_mem(struct hl_device *hdev, diff --git a/drivers/misc/habanalabs/gaudi/gaudi_coresight.c b/drivers/misc/habanalabs/gaudi/gaudi_coresight.c index bf0e062d7b87..c32322cb1728 100644 --- a/drivers/misc/habanalabs/gaudi/gaudi_coresight.c +++ b/drivers/misc/habanalabs/gaudi/gaudi_coresight.c @@ -392,6 +392,7 @@ static int gaudi_config_stm(struct hl_device *hdev, { struct hl_debug_params_stm *input; u64 base_reg; + u32 frequency; int rc; if (params->reg_idx >= ARRAY_SIZE(debug_stm_regs)) { @@ -420,7 +421,10 @@ static int gaudi_config_stm(struct hl_device *hdev, WREG32(base_reg + 0xE00, lower_32_bits(input->sp_mask)); WREG32(base_reg + 0xEF4, input->id); WREG32(base_reg + 0xDF4, 0x80); - WREG32(base_reg + 0xE8C, input->frequency); + frequency = hdev->asic_prop.psoc_timestamp_frequency; + if (frequency == 0) + frequency = input->frequency; + WREG32(base_reg + 0xE8C, frequency); WREG32(base_reg + 0xE90, 0x7FF); /* SW-2176 - SW WA for HW bug */ diff --git a/drivers/misc/habanalabs/goya/goya.c b/drivers/misc/habanalabs/goya/goya.c index ff9e8a31ced4..ff32a8fa7624 100644 --- a/drivers/misc/habanalabs/goya/goya.c +++ b/drivers/misc/habanalabs/goya/goya.c @@ -594,11 +594,36 @@ static void goya_qman0_set_security(struct hl_device *hdev, bool secure) static void goya_fetch_psoc_frequency(struct hl_device *hdev) { struct asic_fixed_properties *prop = &hdev->asic_prop; + u32 trace_freq = 0; + u32 pll_clk = 0; + u32 div_fctr = RREG32(mmPSOC_PCI_PLL_DIV_FACTOR_1); + u32 div_sel = RREG32(mmPSOC_PCI_PLL_DIV_SEL_1); + u32 nr = RREG32(mmPSOC_PCI_PLL_NR); + u32 nf = RREG32(mmPSOC_PCI_PLL_NF); + u32 od = RREG32(mmPSOC_PCI_PLL_OD); + + if (div_sel == DIV_SEL_REF_CLK || div_sel == DIV_SEL_DIVIDED_REF) { + if (div_sel == DIV_SEL_REF_CLK) + trace_freq = PLL_REF_CLK; + else + trace_freq = PLL_REF_CLK / (div_fctr + 1); + } else if (div_sel == DIV_SEL_PLL_CLK || + div_sel == DIV_SEL_DIVIDED_PLL) { + pll_clk = PLL_REF_CLK * (nf + 1) / ((nr + 1) * (od + 1)); + if (div_sel == DIV_SEL_PLL_CLK) + trace_freq = pll_clk; + else + trace_freq = pll_clk / (div_fctr + 1); + } else { + dev_warn(hdev->dev, + "Received invalid div select value: %d", div_sel); + } - prop->psoc_pci_pll_nr = RREG32(mmPSOC_PCI_PLL_NR); - prop->psoc_pci_pll_nf = RREG32(mmPSOC_PCI_PLL_NF); - prop->psoc_pci_pll_od = RREG32(mmPSOC_PCI_PLL_OD); - prop->psoc_pci_pll_div_factor = RREG32(mmPSOC_PCI_PLL_DIV_FACTOR_1); + prop->psoc_timestamp_frequency = trace_freq; + prop->psoc_pci_pll_nr = nr; + prop->psoc_pci_pll_nf = nf; + prop->psoc_pci_pll_od = od; + prop->psoc_pci_pll_div_factor = div_fctr; } int goya_late_init(struct hl_device *hdev) diff --git a/drivers/misc/habanalabs/goya/goya_coresight.c b/drivers/misc/habanalabs/goya/goya_coresight.c index aa51fc71f0a1..18e12e9d284b 100644 --- a/drivers/misc/habanalabs/goya/goya_coresight.c +++ b/drivers/misc/habanalabs/goya/goya_coresight.c @@ -232,6 +232,7 @@ static int goya_config_stm(struct hl_device *hdev, { struct hl_debug_params_stm *input; u64 base_reg; + u32 frequency; int rc; if (params->reg_idx >= ARRAY_SIZE(debug_stm_regs)) { @@ -264,7 +265,10 @@ static int goya_config_stm(struct hl_device *hdev, WREG32(base_reg + 0xE20, 0xFFFFFFFF); WREG32(base_reg + 0xEF4, input->id); WREG32(base_reg + 0xDF4, 0x80); - WREG32(base_reg + 0xE8C, input->frequency); + frequency = hdev->asic_prop.psoc_timestamp_frequency; + if (frequency == 0) + frequency = input->frequency; + WREG32(base_reg + 0xE8C, frequency); WREG32(base_reg + 0xE90, 0x7FF); WREG32(base_reg + 0xE80, 0x27 | (input->id << 16)); } else { diff --git a/drivers/misc/habanalabs/habanalabs.h b/drivers/misc/habanalabs/habanalabs.h index 8cd4b55d0608..4e68a41cce77 100644 --- a/drivers/misc/habanalabs/habanalabs.h +++ b/drivers/misc/habanalabs/habanalabs.h @@ -247,6 +247,7 @@ struct hl_mmu_properties { * @psoc_pci_pll_nf: PCI PLL NF value. * @psoc_pci_pll_od: PCI PLL OD value. * @psoc_pci_pll_div_factor: PCI PLL DIV FACTOR 1 value. + * @psoc_timestamp_frequency: frequency of the psoc timestamp clock. * @high_pll: high PLL frequency used by the device. * @cb_pool_cb_cnt: number of CBs in the CB pool. * @cb_pool_cb_size: size of each CB in the CB pool. @@ -291,6 +292,7 @@ struct asic_fixed_properties { u32 psoc_pci_pll_nf; u32 psoc_pci_pll_od; u32 psoc_pci_pll_div_factor; + u32 psoc_timestamp_frequency; u32 high_pll; u32 cb_pool_cb_cnt; u32 cb_pool_cb_size; @@ -533,6 +535,15 @@ enum hl_pll_frequency { PLL_LAST }; +#define PLL_REF_CLK 50 + +enum div_select_defs { + DIV_SEL_REF_CLK = 0, + DIV_SEL_PLL_CLK = 1, + DIV_SEL_DIVIDED_REF = 2, + DIV_SEL_DIVIDED_PLL = 3, +}; + /** * struct hl_asic_funcs - ASIC specific functions that are can be called from * common code. diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/gaudi_regs.h b/drivers/misc/habanalabs/include/gaudi/asic_reg/gaudi_regs.h index 85e3b5148595..62078077aee5 100644 --- a/drivers/misc/habanalabs/include/gaudi/asic_reg/gaudi_regs.h +++ b/drivers/misc/habanalabs/include/gaudi/asic_reg/gaudi_regs.h @@ -91,6 +91,7 @@ #include "psoc_pci_pll_regs.h" #include "psoc_hbm_pll_regs.h" +#include "psoc_cpu_pll_regs.h" #define GAUDI_ECC_MEM_SEL_OFFSET 0xF18 #define GAUDI_ECC_ADDRESS_OFFSET 0xF1C diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/psoc_cpu_pll_regs.h b/drivers/misc/habanalabs/include/gaudi/asic_reg/psoc_cpu_pll_regs.h new file mode 100644 index 000000000000..2585c70f59ef --- /dev/null +++ b/drivers/misc/habanalabs/include/gaudi/asic_reg/psoc_cpu_pll_regs.h @@ -0,0 +1,114 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright 2016-2018 HabanaLabs, Ltd. + * All Rights Reserved. + * + */ + +/************************************ + ** This is an auto-generated file ** + ** DO NOT EDIT BELOW ** + ************************************/ + +#ifndef ASIC_REG_PSOC_CPU_PLL_REGS_H_ +#define ASIC_REG_PSOC_CPU_PLL_REGS_H_ + +/* + ***************************************** + * PSOC_CPU_PLL (Prototype: PLL) + ***************************************** + */ + +#define mmPSOC_CPU_PLL_NR 0xC70100 + +#define mmPSOC_CPU_PLL_NF 0xC70104 + +#define mmPSOC_CPU_PLL_OD 0xC70108 + +#define mmPSOC_CPU_PLL_NB 0xC7010C + +#define mmPSOC_CPU_PLL_CFG 0xC70110 + +#define mmPSOC_CPU_PLL_LOSE_MASK 0xC70120 + +#define mmPSOC_CPU_PLL_LOCK_INTR 0xC70128 + +#define mmPSOC_CPU_PLL_LOCK_BYPASS 0xC7012C + +#define mmPSOC_CPU_PLL_DATA_CHNG 0xC70130 + +#define mmPSOC_CPU_PLL_RST 0xC70134 + +#define mmPSOC_CPU_PLL_SLIP_WD_CNTR 0xC70150 + +#define mmPSOC_CPU_PLL_DIV_FACTOR_0 0xC70200 + +#define mmPSOC_CPU_PLL_DIV_FACTOR_1 0xC70204 + +#define mmPSOC_CPU_PLL_DIV_FACTOR_2 0xC70208 + +#define mmPSOC_CPU_PLL_DIV_FACTOR_3 0xC7020C + +#define mmPSOC_CPU_PLL_DIV_FACTOR_CMD_0 0xC70220 + +#define mmPSOC_CPU_PLL_DIV_FACTOR_CMD_1 0xC70224 + +#define mmPSOC_CPU_PLL_DIV_FACTOR_CMD_2 0xC70228 + +#define mmPSOC_CPU_PLL_DIV_FACTOR_CMD_3 0xC7022C + +#define mmPSOC_CPU_PLL_DIV_SEL_0 0xC70280 + +#define mmPSOC_CPU_PLL_DIV_SEL_1 0xC70284 + +#define mmPSOC_CPU_PLL_DIV_SEL_2 0xC70288 + +#define mmPSOC_CPU_PLL_DIV_SEL_3 0xC7028C + +#define mmPSOC_CPU_PLL_DIV_EN_0 0xC702A0 + +#define mmPSOC_CPU_PLL_DIV_EN_1 0xC702A4 + +#define mmPSOC_CPU_PLL_DIV_EN_2 0xC702A8 + +#define mmPSOC_CPU_PLL_DIV_EN_3 0xC702AC + +#define mmPSOC_CPU_PLL_DIV_FACTOR_BUSY_0 0xC702C0 + +#define mmPSOC_CPU_PLL_DIV_FACTOR_BUSY_1 0xC702C4 + +#define mmPSOC_CPU_PLL_DIV_FACTOR_BUSY_2 0xC702C8 + +#define mmPSOC_CPU_PLL_DIV_FACTOR_BUSY_3 0xC702CC + +#define mmPSOC_CPU_PLL_CLK_GATER 0xC70300 + +#define mmPSOC_CPU_PLL_CLK_RLX_0 0xC70310 + +#define mmPSOC_CPU_PLL_CLK_RLX_1 0xC70314 + +#define mmPSOC_CPU_PLL_CLK_RLX_2 0xC70318 + +#define mmPSOC_CPU_PLL_CLK_RLX_3 0xC7031C + +#define mmPSOC_CPU_PLL_REF_CNTR_PERIOD 0xC70400 + +#define mmPSOC_CPU_PLL_REF_LOW_THRESHOLD 0xC70410 + +#define mmPSOC_CPU_PLL_REF_HIGH_THRESHOLD 0xC70420 + +#define mmPSOC_CPU_PLL_PLL_NOT_STABLE 0xC70430 + +#define mmPSOC_CPU_PLL_FREQ_CALC_EN 0xC70440 + +#define mmPSOC_CPU_PLL_RLX_BITMAP_CFG 0xC70500 + +#define mmPSOC_CPU_PLL_RLX_BITMAP_0 0xC70510 + +#define mmPSOC_CPU_PLL_RLX_BITMAP_1 0xC70514 + +#define mmPSOC_CPU_PLL_RLX_BITMAP_2 0xC70518 + +#define mmPSOC_CPU_PLL_RLX_BITMAP_3 0xC7051C + +#endif /* ASIC_REG_PSOC_CPU_PLL_REGS_H_ */ -- cgit v1.2.3-59-g8ed1b From 917b79b09671740ce2e2c702641e50f890b27a67 Mon Sep 17 00:00:00 2001 From: Omer Shpigelman Date: Sat, 4 Jul 2020 22:51:16 +0300 Subject: habanalabs: rephrase error message Rephrase F/W error message to make it more understandable to ordinary users. Signed-off-by: Omer Shpigelman Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/firmware_if.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/misc/habanalabs/firmware_if.c b/drivers/misc/habanalabs/firmware_if.c index 15e0793da655..6900c01d060f 100644 --- a/drivers/misc/habanalabs/firmware_if.c +++ b/drivers/misc/habanalabs/firmware_if.c @@ -569,7 +569,8 @@ int hl_fw_init_cpu(struct hl_device *hdev, u32 cpu_boot_status_reg, "Device reports FIT image is corrupted\n"); else dev_err(hdev->dev, - "Device failed to load, %d\n", status); + "Failed to load firmware to device, %d\n", + status); rc = -EIO; goto out; -- cgit v1.2.3-59-g8ed1b From dd9efabd0a5ae6dab255737c808d5ab6af1e8f91 Mon Sep 17 00:00:00 2001 From: Ofir Bitton Date: Mon, 15 Jun 2020 12:09:50 +0300 Subject: habanalabs: Increase queues depth After recent concurrent cs amount increase, we must also increase queues depth since much more concurrent work can be done. All external queue depths were increased to 4096 as gaudi's internal queue depths were also increased to 1024. Signed-off-by: Ofir Bitton Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/gaudi/gaudiP.h | 6 +++--- drivers/misc/habanalabs/habanalabs.h | 31 ++++--------------------------- drivers/misc/habanalabs/hw_queue.c | 2 -- drivers/misc/habanalabs/irq.c | 4 ---- 4 files changed, 7 insertions(+), 36 deletions(-) diff --git a/drivers/misc/habanalabs/gaudi/gaudiP.h b/drivers/misc/habanalabs/gaudi/gaudiP.h index 3958fe38c8ee..bdc5f96085a7 100644 --- a/drivers/misc/habanalabs/gaudi/gaudiP.h +++ b/drivers/misc/habanalabs/gaudi/gaudiP.h @@ -123,14 +123,14 @@ /* Internal QMANs PQ sizes */ -#define MME_QMAN_LENGTH 64 +#define MME_QMAN_LENGTH 1024 #define MME_QMAN_SIZE_IN_BYTES (MME_QMAN_LENGTH * QMAN_PQ_ENTRY_SIZE) -#define HBM_DMA_QMAN_LENGTH 64 +#define HBM_DMA_QMAN_LENGTH 1024 #define HBM_DMA_QMAN_SIZE_IN_BYTES \ (HBM_DMA_QMAN_LENGTH * QMAN_PQ_ENTRY_SIZE) -#define TPC_QMAN_LENGTH 64 +#define TPC_QMAN_LENGTH 1024 #define TPC_QMAN_SIZE_IN_BYTES (TPC_QMAN_LENGTH * QMAN_PQ_ENTRY_SIZE) #define SRAM_USER_BASE_OFFSET GAUDI_DRIVER_SRAM_RESERVED_SIZE_FROM_START diff --git a/drivers/misc/habanalabs/habanalabs.h b/drivers/misc/habanalabs/habanalabs.h index 4e68a41cce77..e4d6f7c91194 100644 --- a/drivers/misc/habanalabs/habanalabs.h +++ b/drivers/misc/habanalabs/habanalabs.h @@ -378,38 +378,15 @@ struct hl_cb { struct hl_cs_job; -/* - * Currently, there are two limitations on the maximum length of a queue: - * - * 1. The memory footprint of the queue. The current allocated space for the - * queue is PAGE_SIZE. Because each entry in the queue is HL_BD_SIZE, - * the maximum length of the queue can be PAGE_SIZE / HL_BD_SIZE, - * which currently is 4096/16 = 256 entries. - * - * To increase that, we need either to decrease the size of the - * BD (difficult), or allocate more than a single page (easier). - * - * 2. Because the size of the JOB handle field in the BD CTL / completion queue - * is 10-bit, we can have up to 1024 open jobs per hardware queue. - * Therefore, each queue can hold up to 1024 entries. - * - * HL_QUEUE_LENGTH is in units of struct hl_bd. - * HL_QUEUE_LENGTH * sizeof(struct hl_bd) should be <= HL_PAGE_SIZE - */ - -#define HL_PAGE_SIZE 4096 /* minimum page size */ -/* Must be power of 2 (HL_PAGE_SIZE / HL_BD_SIZE) */ -#define HL_QUEUE_LENGTH 256 +/* Queue length of external and HW queues */ +#define HL_QUEUE_LENGTH 4096 #define HL_QUEUE_SIZE_IN_BYTES (HL_QUEUE_LENGTH * HL_BD_SIZE) -/* - * HL_CQ_LENGTH is in units of struct hl_cq_entry. - * HL_CQ_LENGTH should be <= HL_PAGE_SIZE - */ +/* HL_CQ_LENGTH is in units of struct hl_cq_entry */ #define HL_CQ_LENGTH HL_QUEUE_LENGTH #define HL_CQ_SIZE_IN_BYTES (HL_CQ_LENGTH * HL_CQ_ENTRY_SIZE) -/* Must be power of 2 (HL_PAGE_SIZE / HL_EQ_ENTRY_SIZE) */ +/* Must be power of 2 */ #define HL_EQ_LENGTH 64 #define HL_EQ_SIZE_IN_BYTES (HL_EQ_LENGTH * HL_EQ_ENTRY_SIZE) diff --git a/drivers/misc/habanalabs/hw_queue.c b/drivers/misc/habanalabs/hw_queue.c index 27f0c34b63b9..f5a10a5ac300 100644 --- a/drivers/misc/habanalabs/hw_queue.c +++ b/drivers/misc/habanalabs/hw_queue.c @@ -780,8 +780,6 @@ static int queue_init(struct hl_device *hdev, struct hl_hw_queue *q, { int rc; - BUILD_BUG_ON(HL_QUEUE_SIZE_IN_BYTES > HL_PAGE_SIZE); - q->hw_queue_id = hw_queue_id; switch (q->queue_type) { diff --git a/drivers/misc/habanalabs/irq.c b/drivers/misc/habanalabs/irq.c index 6981d67153b1..7a4878edb1a3 100644 --- a/drivers/misc/habanalabs/irq.c +++ b/drivers/misc/habanalabs/irq.c @@ -220,8 +220,6 @@ int hl_cq_init(struct hl_device *hdev, struct hl_cq *q, u32 hw_queue_id) { void *p; - BUILD_BUG_ON(HL_CQ_SIZE_IN_BYTES > HL_PAGE_SIZE); - p = hdev->asic_funcs->asic_dma_alloc_coherent(hdev, HL_CQ_SIZE_IN_BYTES, &q->bus_address, GFP_KERNEL | __GFP_ZERO); if (!p) @@ -282,8 +280,6 @@ int hl_eq_init(struct hl_device *hdev, struct hl_eq *q) { void *p; - BUILD_BUG_ON(HL_EQ_SIZE_IN_BYTES > HL_PAGE_SIZE); - p = hdev->asic_funcs->cpu_accessible_dma_pool_alloc(hdev, HL_EQ_SIZE_IN_BYTES, &q->bus_address); -- cgit v1.2.3-59-g8ed1b From 0eab4f89d64c1639a1b9288f316bbfa292bd9413 Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Mon, 22 Jun 2020 09:52:22 +0300 Subject: habanalabs: rephrase error messages rephrase some error/warning/notice messages to make them more accessible to ordinary users. There is no need to print context ASID as the driver currently doesn't support multiple contexts. Signed-off-by: Oded Gabbay Reviewed-by: Tomer Tayar --- drivers/misc/habanalabs/command_submission.c | 20 +++++++++++++------- drivers/misc/habanalabs/context.c | 3 +-- drivers/misc/habanalabs/firmware_if.c | 4 ++-- drivers/misc/habanalabs/memory.c | 3 +-- 4 files changed, 17 insertions(+), 13 deletions(-) diff --git a/drivers/misc/habanalabs/command_submission.c b/drivers/misc/habanalabs/command_submission.c index 62dab99dda98..f81d6685e011 100644 --- a/drivers/misc/habanalabs/command_submission.c +++ b/drivers/misc/habanalabs/command_submission.c @@ -373,9 +373,9 @@ static void cs_timedout(struct work_struct *work) hdev = cs->ctx->hdev; ctx_asid = cs->ctx->asid; - /* TODO: add information about last signaled seq and last emitted seq */ - dev_err(hdev->dev, "User %d command submission %llu got stuck!\n", - ctx_asid, cs->sequence); + dev_err(hdev->dev, + "Command submission %llu has not finished in time!\n", + cs->sequence); cs_put(cs); @@ -1130,7 +1130,7 @@ static long _hl_cs_wait_ioctl(struct hl_device *hdev, rc = PTR_ERR(fence); if (rc == -EINVAL) dev_notice_ratelimited(hdev->dev, - "Can't wait on seq %llu because current CS is at seq %llu\n", + "Can't wait on CS %llu because current CS is at seq %llu\n", seq, ctx->cs_sequence); } else if (fence) { rc = dma_fence_wait_timeout(fence, true, timeout); @@ -1163,15 +1163,21 @@ int hl_cs_wait_ioctl(struct hl_fpriv *hpriv, void *data) memset(args, 0, sizeof(*args)); if (rc < 0) { - dev_err_ratelimited(hdev->dev, - "Error %ld on waiting for CS handle %llu\n", - rc, seq); if (rc == -ERESTARTSYS) { + dev_err_ratelimited(hdev->dev, + "user process got signal while waiting for CS handle %llu\n", + seq); args->out.status = HL_WAIT_CS_STATUS_INTERRUPTED; rc = -EINTR; } else if (rc == -ETIMEDOUT) { + dev_err_ratelimited(hdev->dev, + "CS %llu has timed-out while user process is waiting for it\n", + seq); args->out.status = HL_WAIT_CS_STATUS_TIMEDOUT; } else if (rc == -EIO) { + dev_err_ratelimited(hdev->dev, + "CS %llu has been aborted while user process is waiting for it\n", + seq); args->out.status = HL_WAIT_CS_STATUS_ABORTED; } return rc; diff --git a/drivers/misc/habanalabs/context.c b/drivers/misc/habanalabs/context.c index 1b96fefa4a65..1e3e5b19ecd9 100644 --- a/drivers/misc/habanalabs/context.c +++ b/drivers/misc/habanalabs/context.c @@ -112,8 +112,7 @@ void hl_ctx_free(struct hl_device *hdev, struct hl_ctx *ctx) return; dev_warn(hdev->dev, - "Context %d closed or terminated but its CS are executing\n", - ctx->asid); + "user process released device but its command submissions are still executing\n"); } int hl_ctx_init(struct hl_device *hdev, struct hl_ctx *ctx, bool is_kernel_ctx) diff --git a/drivers/misc/habanalabs/firmware_if.c b/drivers/misc/habanalabs/firmware_if.c index 6900c01d060f..9e7f203a09d7 100644 --- a/drivers/misc/habanalabs/firmware_if.c +++ b/drivers/misc/habanalabs/firmware_if.c @@ -289,7 +289,7 @@ int hl_fw_armcp_info_get(struct hl_device *hdev) HL_ARMCP_INFO_TIMEOUT_USEC, &result); if (rc) { dev_err(hdev->dev, - "Failed to send ArmCP info pkt, error %d\n", rc); + "Failed to handle ArmCP info pkt, error %d\n", rc); goto out; } @@ -340,7 +340,7 @@ int hl_fw_get_eeprom_data(struct hl_device *hdev, void *data, size_t max_size) if (rc) { dev_err(hdev->dev, - "Failed to send ArmCP EEPROM packet, error %d\n", rc); + "Failed to handle ArmCP EEPROM packet, error %d\n", rc); goto out; } diff --git a/drivers/misc/habanalabs/memory.c b/drivers/misc/habanalabs/memory.c index 47da84a17719..e4e1693e5c6c 100644 --- a/drivers/misc/habanalabs/memory.c +++ b/drivers/misc/habanalabs/memory.c @@ -1730,8 +1730,7 @@ void hl_vm_ctx_fini(struct hl_ctx *ctx) */ if (!hdev->hard_reset_pending && !hash_empty(ctx->mem_hash)) dev_notice(hdev->dev, - "ctx %d is freed while it has va in use\n", - ctx->asid); + "user released device without removing its memory mappings\n"); hash_for_each_safe(ctx->mem_hash, i, tmp_node, hnode, node) { dev_dbg(hdev->dev, -- cgit v1.2.3-59-g8ed1b From c8f9b49d2db7e98df534c6e666b26c64da4cb667 Mon Sep 17 00:00:00 2001 From: Christine Gharzuzi Date: Tue, 23 Jun 2020 19:21:22 +0300 Subject: habanalabs: extract cpu boot status lookup Extract detection of the cpu boot status to a function to allow code reuse Signed-off-by: Christine Gharzuzi Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/firmware_if.c | 92 ++++++++++++++++++----------------- 1 file changed, 48 insertions(+), 44 deletions(-) diff --git a/drivers/misc/habanalabs/firmware_if.c b/drivers/misc/habanalabs/firmware_if.c index 9e7f203a09d7..3be1549cd137 100644 --- a/drivers/misc/habanalabs/firmware_if.c +++ b/drivers/misc/habanalabs/firmware_if.c @@ -393,6 +393,53 @@ static void fw_read_errors(struct hl_device *hdev, u32 boot_err0_reg) "Device boot error - NIC F/W initialization failed\n"); } +static void hl_detect_cpu_boot_status(struct hl_device *hdev, u32 status) +{ + switch (status) { + case CPU_BOOT_STATUS_NA: + dev_err(hdev->dev, + "Device boot error - BTL did NOT run\n"); + break; + case CPU_BOOT_STATUS_IN_WFE: + dev_err(hdev->dev, + "Device boot error - Stuck inside WFE loop\n"); + break; + case CPU_BOOT_STATUS_IN_BTL: + dev_err(hdev->dev, + "Device boot error - Stuck in BTL\n"); + break; + case CPU_BOOT_STATUS_IN_PREBOOT: + dev_err(hdev->dev, + "Device boot error - Stuck in Preboot\n"); + break; + case CPU_BOOT_STATUS_IN_SPL: + dev_err(hdev->dev, + "Device boot error - Stuck in SPL\n"); + break; + case CPU_BOOT_STATUS_IN_UBOOT: + dev_err(hdev->dev, + "Device boot error - Stuck in u-boot\n"); + break; + case CPU_BOOT_STATUS_DRAM_INIT_FAIL: + dev_err(hdev->dev, + "Device boot error - DRAM initialization failed\n"); + break; + case CPU_BOOT_STATUS_UBOOT_NOT_READY: + dev_err(hdev->dev, + "Device boot error - u-boot stopped by user\n"); + break; + case CPU_BOOT_STATUS_TS_INIT_FAIL: + dev_err(hdev->dev, + "Device boot error - Thermal Sensor initialization failed\n"); + break; + default: + dev_err(hdev->dev, + "Device boot error - Invalid status code %d\n", + status); + break; + } +} + int hl_fw_init_cpu(struct hl_device *hdev, u32 cpu_boot_status_reg, u32 msg_to_cpu_reg, u32 cpu_msg_status_reg, u32 boot_err0_reg, bool skip_bmc, @@ -466,50 +513,7 @@ int hl_fw_init_cpu(struct hl_device *hdev, u32 cpu_boot_status_reg, * versions but we keep them here for backward compatibility */ if (rc) { - switch (status) { - case CPU_BOOT_STATUS_NA: - dev_err(hdev->dev, - "Device boot error - BTL did NOT run\n"); - break; - case CPU_BOOT_STATUS_IN_WFE: - dev_err(hdev->dev, - "Device boot error - Stuck inside WFE loop\n"); - break; - case CPU_BOOT_STATUS_IN_BTL: - dev_err(hdev->dev, - "Device boot error - Stuck in BTL\n"); - break; - case CPU_BOOT_STATUS_IN_PREBOOT: - dev_err(hdev->dev, - "Device boot error - Stuck in Preboot\n"); - break; - case CPU_BOOT_STATUS_IN_SPL: - dev_err(hdev->dev, - "Device boot error - Stuck in SPL\n"); - break; - case CPU_BOOT_STATUS_IN_UBOOT: - dev_err(hdev->dev, - "Device boot error - Stuck in u-boot\n"); - break; - case CPU_BOOT_STATUS_DRAM_INIT_FAIL: - dev_err(hdev->dev, - "Device boot error - DRAM initialization failed\n"); - break; - case CPU_BOOT_STATUS_UBOOT_NOT_READY: - dev_err(hdev->dev, - "Device boot error - u-boot stopped by user\n"); - break; - case CPU_BOOT_STATUS_TS_INIT_FAIL: - dev_err(hdev->dev, - "Device boot error - Thermal Sensor initialization failed\n"); - break; - default: - dev_err(hdev->dev, - "Device boot error - Invalid status code %d\n", - status); - break; - } - + hl_detect_cpu_boot_status(hdev, status); rc = -EIO; goto out; } -- cgit v1.2.3-59-g8ed1b From db491e4f08a9fd84ebb1ebd22a6b0b988a81a0d8 Mon Sep 17 00:00:00 2001 From: Ofir Bitton Date: Thu, 18 Jun 2020 09:51:16 +0300 Subject: habanalabs: Add dropped cs statistics info struct Add command submission statistics structure which can be obtained through the info ioctl. Each drop counter describes the reason for which the command submission was dropped. This information is needed for the user to be aware of the specific reason for which the submitted work was dropped. The user can then utilize the driver more efficiently. Signed-off-by: Ofir Bitton Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/command_submission.c | 24 +++++++++++++++++++++++- drivers/misc/habanalabs/habanalabs.h | 5 +++++ drivers/misc/habanalabs/habanalabs_ioctl.c | 24 ++++++++++++++++++++++++ drivers/misc/habanalabs/hw_queue.c | 5 ++++- include/uapi/misc/habanalabs.h | 21 +++++++++++++++++++++ 5 files changed, 77 insertions(+), 2 deletions(-) diff --git a/drivers/misc/habanalabs/command_submission.c b/drivers/misc/habanalabs/command_submission.c index f81d6685e011..777f88d25acd 100644 --- a/drivers/misc/habanalabs/command_submission.c +++ b/drivers/misc/habanalabs/command_submission.c @@ -246,6 +246,18 @@ static void free_job(struct hl_device *hdev, struct hl_cs_job *job) kfree(job); } +static void cs_counters_aggregate(struct hl_device *hdev, struct hl_ctx *ctx) +{ + hdev->aggregated_cs_counters.device_in_reset_drop_cnt += + ctx->cs_counters.device_in_reset_drop_cnt; + hdev->aggregated_cs_counters.out_of_mem_drop_cnt += + ctx->cs_counters.out_of_mem_drop_cnt; + hdev->aggregated_cs_counters.parsing_drop_cnt += + ctx->cs_counters.parsing_drop_cnt; + hdev->aggregated_cs_counters.queue_full_drop_cnt += + ctx->cs_counters.queue_full_drop_cnt; +} + static void cs_do_release(struct kref *ref) { struct hl_cs *cs = container_of(ref, struct hl_cs, @@ -349,6 +361,8 @@ static void cs_do_release(struct kref *ref) dma_fence_signal(cs->fence); dma_fence_put(cs->fence); + cs_counters_aggregate(hdev, cs->ctx); + kfree(cs); } @@ -632,12 +646,15 @@ static int cs_ioctl_default(struct hl_fpriv *hpriv, void __user *chunks, rc = validate_queue_index(hdev, chunk, &queue_type, &is_kernel_allocated_cb); - if (rc) + if (rc) { + hpriv->ctx->cs_counters.parsing_drop_cnt++; goto free_cs_object; + } if (is_kernel_allocated_cb) { cb = get_cb_from_cs_chunk(hdev, &hpriv->cb_mgr, chunk); if (!cb) { + hpriv->ctx->cs_counters.parsing_drop_cnt++; rc = -EINVAL; goto free_cs_object; } @@ -651,6 +668,7 @@ static int cs_ioctl_default(struct hl_fpriv *hpriv, void __user *chunks, job = hl_cs_allocate_job(hdev, queue_type, is_kernel_allocated_cb); if (!job) { + hpriv->ctx->cs_counters.out_of_mem_drop_cnt++; dev_err(hdev->dev, "Failed to allocate a new job\n"); rc = -ENOMEM; if (is_kernel_allocated_cb) @@ -683,6 +701,7 @@ static int cs_ioctl_default(struct hl_fpriv *hpriv, void __user *chunks, rc = cs_parser(hpriv, job); if (rc) { + hpriv->ctx->cs_counters.parsing_drop_cnt++; dev_err(hdev->dev, "Failed to parse JOB %d.%llu.%d, err %d, rejecting the CS\n", cs->ctx->asid, cs->sequence, job->id, rc); @@ -691,6 +710,7 @@ static int cs_ioctl_default(struct hl_fpriv *hpriv, void __user *chunks, } if (int_queues_only) { + hpriv->ctx->cs_counters.parsing_drop_cnt++; dev_err(hdev->dev, "Reject CS %d.%llu because only internal queues jobs are present\n", cs->ctx->asid, cs->sequence); @@ -875,6 +895,7 @@ static int cs_ioctl_signal_wait(struct hl_fpriv *hpriv, enum hl_cs_type cs_type, job = hl_cs_allocate_job(hdev, q_type, true); if (!job) { + ctx->cs_counters.out_of_mem_drop_cnt++; dev_err(hdev->dev, "Failed to allocate a new job\n"); rc = -ENOMEM; goto put_cs; @@ -882,6 +903,7 @@ static int cs_ioctl_signal_wait(struct hl_fpriv *hpriv, enum hl_cs_type cs_type, cb = hl_cb_kernel_create(hdev, PAGE_SIZE); if (!cb) { + ctx->cs_counters.out_of_mem_drop_cnt++; kfree(job); rc = -EFAULT; goto put_cs; diff --git a/drivers/misc/habanalabs/habanalabs.h b/drivers/misc/habanalabs/habanalabs.h index e4d6f7c91194..ae781453a509 100644 --- a/drivers/misc/habanalabs/habanalabs.h +++ b/drivers/misc/habanalabs/habanalabs.h @@ -10,6 +10,7 @@ #include "include/armcp_if.h" #include "include/qman_if.h" +#include #include #include @@ -787,6 +788,7 @@ struct hl_ctx { struct mutex mem_hash_lock; struct mutex mmu_lock; struct list_head debugfs_list; + struct hl_cs_counters cs_counters; u64 cs_sequence; u64 *dram_default_hops; spinlock_t cs_lock; @@ -1391,6 +1393,7 @@ struct hl_device_idle_busy_ts { * @compute_ctx: current compute context executing. * @idle_busy_ts_arr: array to hold time stamps of transitions from idle to busy * and vice-versa + * @aggregated_cs_counters: aggregated cs counters among all contexts * @dram_used_mem: current DRAM memory consumption. * @timeout_jiffies: device CS timeout value. * @max_power: the max power of the device, as configured by the sysadmin. This @@ -1489,6 +1492,8 @@ struct hl_device { struct hl_device_idle_busy_ts *idle_busy_ts_arr; + struct hl_cs_counters aggregated_cs_counters; + atomic64_t dram_used_mem; u64 timeout_jiffies; u64 max_power; diff --git a/drivers/misc/habanalabs/habanalabs_ioctl.c b/drivers/misc/habanalabs/habanalabs_ioctl.c index 52eedd3a6c3a..5af1c03da473 100644 --- a/drivers/misc/habanalabs/habanalabs_ioctl.c +++ b/drivers/misc/habanalabs/habanalabs_ioctl.c @@ -276,6 +276,27 @@ static int time_sync_info(struct hl_device *hdev, struct hl_info_args *args) min((size_t) max_size, sizeof(time_sync))) ? -EFAULT : 0; } +static int cs_counters_info(struct hl_fpriv *hpriv, struct hl_info_args *args) +{ + struct hl_device *hdev = hpriv->hdev; + struct hl_info_cs_counters cs_counters = {0}; + u32 max_size = args->return_size; + void __user *out = (void __user *) (uintptr_t) args->return_pointer; + + if ((!max_size) || (!out)) + return -EINVAL; + + memcpy(&cs_counters.cs_counters, &hdev->aggregated_cs_counters, + sizeof(struct hl_cs_counters)); + + if (hpriv->ctx) + memcpy(&cs_counters.ctx_cs_counters, &hpriv->ctx->cs_counters, + sizeof(struct hl_cs_counters)); + + return copy_to_user(out, &cs_counters, + min((size_t) max_size, sizeof(cs_counters))) ? -EFAULT : 0; +} + static int _hl_info_ioctl(struct hl_fpriv *hpriv, void *data, struct device *dev) { @@ -336,6 +357,9 @@ static int _hl_info_ioctl(struct hl_fpriv *hpriv, void *data, case HL_INFO_TIME_SYNC: return time_sync_info(hdev, args); + case HL_INFO_CS_COUNTERS: + return cs_counters_info(hpriv, args); + default: dev_err(dev, "Invalid request %d\n", args->op); rc = -ENOTTY; diff --git a/drivers/misc/habanalabs/hw_queue.c b/drivers/misc/habanalabs/hw_queue.c index f5a10a5ac300..da66ffb528f8 100644 --- a/drivers/misc/habanalabs/hw_queue.c +++ b/drivers/misc/habanalabs/hw_queue.c @@ -514,6 +514,7 @@ int hl_hw_queue_schedule_cs(struct hl_cs *cs) hdev->asic_funcs->hw_queues_lock(hdev); if (hl_device_disabled_or_in_reset(hdev)) { + ctx->cs_counters.device_in_reset_drop_cnt++; dev_err(hdev->dev, "device is disabled or in reset, CS rejected!\n"); rc = -EPERM; @@ -543,8 +544,10 @@ int hl_hw_queue_schedule_cs(struct hl_cs *cs) break; } - if (rc) + if (rc) { + ctx->cs_counters.queue_full_drop_cnt++; goto unroll_cq_resv; + } if (q->queue_type == QUEUE_TYPE_EXT || q->queue_type == QUEUE_TYPE_HW) diff --git a/include/uapi/misc/habanalabs.h b/include/uapi/misc/habanalabs.h index f218d1c62c62..d5c4f983b7a8 100644 --- a/include/uapi/misc/habanalabs.h +++ b/include/uapi/misc/habanalabs.h @@ -263,6 +263,7 @@ enum hl_device_status { * time the driver was loaded. * HL_INFO_TIME_SYNC - Retrieve the device's time alongside the host's time * for synchronization. + * HL_INFO_CS_COUNTERS - Retrieve command submission counters */ #define HL_INFO_HW_IP_INFO 0 #define HL_INFO_HW_EVENTS 1 @@ -274,6 +275,7 @@ enum hl_device_status { #define HL_INFO_CLK_RATE 8 #define HL_INFO_RESET_COUNT 9 #define HL_INFO_TIME_SYNC 10 +#define HL_INFO_CS_COUNTERS 11 #define HL_INFO_VERSION_MAX_LEN 128 #define HL_INFO_CARD_NAME_MAX_LEN 16 @@ -338,6 +340,25 @@ struct hl_info_time_sync { __u64 host_time; }; +/** + * struct hl_info_cs_counters - command submission counters + * @out_of_mem_drop_cnt: dropped due to memory allocation issue + * @parsing_drop_cnt: dropped due to error in packet parsing + * @queue_full_drop_cnt: dropped due to queue full + * @device_in_reset_drop_cnt: dropped due to device in reset + */ +struct hl_cs_counters { + __u64 out_of_mem_drop_cnt; + __u64 parsing_drop_cnt; + __u64 queue_full_drop_cnt; + __u64 device_in_reset_drop_cnt; +}; + +struct hl_info_cs_counters { + struct hl_cs_counters cs_counters; + struct hl_cs_counters ctx_cs_counters; +}; + struct hl_info_args { /* Location of relevant struct in userspace */ __u64 return_pointer; -- cgit v1.2.3-59-g8ed1b From fcc6a4e606787be775b032f96c57472592f76300 Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Sun, 17 May 2020 08:20:35 +0300 Subject: habanalabs: Extract ECC information from FW ECC (Error Correcting Code) interrupts are going to be handled by the FW. Hence, we define an interface in which the driver can obtain the relevant ECC information. This information is needed for monitoring and can also lead to a hard reset if ECC error is not correctable. Signed-off-by: Ofir Bitton Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/gaudi/gaudi.c | 366 +++++++-------------- drivers/misc/habanalabs/include/armcp_if.h | 12 +- .../habanalabs/include/gaudi/asic_reg/gaudi_regs.h | 19 +- 3 files changed, 146 insertions(+), 251 deletions(-) diff --git a/drivers/misc/habanalabs/gaudi/gaudi.c b/drivers/misc/habanalabs/gaudi/gaudi.c index aa4139626a04..888f42adee6a 100644 --- a/drivers/misc/habanalabs/gaudi/gaudi.c +++ b/drivers/misc/habanalabs/gaudi/gaudi.c @@ -316,6 +316,13 @@ static enum hl_queue_type gaudi_queue_type[GAUDI_QUEUE_ID_SIZE] = { QUEUE_TYPE_NA, /* GAUDI_QUEUE_ID_NIC_9_3 */ }; +struct ecc_info_extract_params { + u64 block_address; + u32 num_memories; + bool derr; + bool disable_clock_gating; +}; + static int gaudi_mmu_update_asid_hop0_addr(struct hl_device *hdev, u32 asid, u64 phys_addr); static int gaudi_send_job_on_qman0(struct hl_device *hdev, @@ -5117,62 +5124,75 @@ static void gaudi_print_mmu_error_info(struct hl_device *hdev) * | |0xF4C memory wrappers 127:96 | * +-------------------+------------------------------------------------------+ */ -static void gaudi_print_ecc_info_generic(struct hl_device *hdev, - const char *block_name, - u64 block_address, int num_memories, - bool derr, bool disable_clock_gating) +static int gaudi_extract_ecc_info(struct hl_device *hdev, + struct ecc_info_extract_params *params, u64 *ecc_address, + u64 *ecc_syndrom, u8 *memory_wrapper_idx) { struct gaudi_device *gaudi = hdev->asic_specific; - int num_mem_regs = num_memories / 32 + ((num_memories % 32) ? 1 : 0); + u32 i, num_mem_regs, reg, err_bit; + u64 err_addr, err_word = 0; + int rc = 0; - if (block_address >= CFG_BASE) - block_address -= CFG_BASE; + num_mem_regs = params->num_memories / 32 + + ((params->num_memories % 32) ? 1 : 0); - if (derr) - block_address += GAUDI_ECC_DERR0_OFFSET; + if (params->block_address >= CFG_BASE) + params->block_address -= CFG_BASE; + + if (params->derr) + err_addr = params->block_address + GAUDI_ECC_DERR0_OFFSET; else - block_address += GAUDI_ECC_SERR0_OFFSET; + err_addr = params->block_address + GAUDI_ECC_SERR0_OFFSET; - if (disable_clock_gating) { + if (params->disable_clock_gating) { mutex_lock(&gaudi->clk_gate_mutex); hdev->asic_funcs->disable_clock_gating(hdev); } - switch (num_mem_regs) { - case 1: - dev_err(hdev->dev, - "%s ECC indication: 0x%08x\n", - block_name, RREG32(block_address)); - break; - case 2: - dev_err(hdev->dev, - "%s ECC indication: 0x%08x 0x%08x\n", - block_name, - RREG32(block_address), RREG32(block_address + 4)); - break; - case 3: - dev_err(hdev->dev, - "%s ECC indication: 0x%08x 0x%08x 0x%08x\n", - block_name, - RREG32(block_address), RREG32(block_address + 4), - RREG32(block_address + 8)); - break; - case 4: - dev_err(hdev->dev, - "%s ECC indication: 0x%08x 0x%08x 0x%08x 0x%08x\n", - block_name, - RREG32(block_address), RREG32(block_address + 4), - RREG32(block_address + 8), RREG32(block_address + 0xc)); - break; - default: - break; + /* Set invalid wrapper index */ + *memory_wrapper_idx = 0xFF; + + /* Iterate through memory wrappers, a single bit must be set */ + for (i = 0 ; i > num_mem_regs ; i++) { + err_addr += i * 4; + err_word = RREG32(err_addr); + if (err_word) { + err_bit = __ffs(err_word); + *memory_wrapper_idx = err_bit + (32 * i); + break; + } + } + if (*memory_wrapper_idx == 0xFF) { + dev_err(hdev->dev, "ECC error information cannot be found\n"); + rc = -EINVAL; + goto enable_clk_gate; } - if (disable_clock_gating) { + WREG32(params->block_address + GAUDI_ECC_MEM_SEL_OFFSET, + *memory_wrapper_idx); + + *ecc_address = + RREG32(params->block_address + GAUDI_ECC_ADDRESS_OFFSET); + *ecc_syndrom = + RREG32(params->block_address + GAUDI_ECC_SYNDROME_OFFSET); + + /* Clear error indication */ + reg = RREG32(params->block_address + GAUDI_ECC_MEM_INFO_CLR_OFFSET); + if (params->derr) + reg |= FIELD_PREP(GAUDI_ECC_MEM_INFO_CLR_DERR_MASK, 1); + else + reg |= FIELD_PREP(GAUDI_ECC_MEM_INFO_CLR_SERR_MASK, 1); + + WREG32(params->block_address + GAUDI_ECC_MEM_INFO_CLR_OFFSET, reg); + +enable_clk_gate: + if (params->disable_clock_gating) { hdev->asic_funcs->enable_clock_gating(hdev); mutex_unlock(&gaudi->clk_gate_mutex); } + + return rc; } static void gaudi_handle_qman_err_generic(struct hl_device *hdev, @@ -5225,239 +5245,99 @@ static void gaudi_handle_qman_err_generic(struct hl_device *hdev, } } -static void gaudi_print_ecc_info(struct hl_device *hdev, u16 event_type) +static void gaudi_handle_ecc_event(struct hl_device *hdev, u16 event_type, + struct hl_eq_ecc_data *ecc_data) { - u64 block_address; - u8 index; - int num_memories; - char desc[32]; - bool derr; - bool disable_clock_gating; + struct ecc_info_extract_params params; + u64 ecc_address = 0, ecc_syndrom = 0; + u8 index, memory_wrapper_idx = 0; + bool extract_info_from_fw; + int rc; switch (event_type) { - case GAUDI_EVENT_PCIE_CORE_SERR: - snprintf(desc, ARRAY_SIZE(desc), "%s", "PCIE_CORE"); - block_address = mmPCIE_CORE_BASE; - num_memories = 51; - derr = false; - disable_clock_gating = false; - break; - case GAUDI_EVENT_PCIE_CORE_DERR: - snprintf(desc, ARRAY_SIZE(desc), "%s", "PCIE_CORE"); - block_address = mmPCIE_CORE_BASE; - num_memories = 51; - derr = true; - disable_clock_gating = false; - break; - case GAUDI_EVENT_PCIE_IF_SERR: - snprintf(desc, ARRAY_SIZE(desc), "%s", "PCIE_WRAP"); - block_address = mmPCIE_WRAP_BASE; - num_memories = 11; - derr = false; - disable_clock_gating = false; - break; - case GAUDI_EVENT_PCIE_IF_DERR: - snprintf(desc, ARRAY_SIZE(desc), "%s", "PCIE_WRAP"); - block_address = mmPCIE_WRAP_BASE; - num_memories = 11; - derr = true; - disable_clock_gating = false; - break; - case GAUDI_EVENT_PCIE_PHY_SERR: - snprintf(desc, ARRAY_SIZE(desc), "%s", "PCIE_PHY"); - block_address = mmPCIE_PHY_BASE; - num_memories = 4; - derr = false; - disable_clock_gating = false; - break; - case GAUDI_EVENT_PCIE_PHY_DERR: - snprintf(desc, ARRAY_SIZE(desc), "%s", "PCIE_PHY"); - block_address = mmPCIE_PHY_BASE; - num_memories = 4; - derr = true; - disable_clock_gating = false; + case GAUDI_EVENT_PCIE_CORE_SERR ... GAUDI_EVENT_PCIE_PHY_DERR: + case GAUDI_EVENT_DMA0_SERR_ECC ... GAUDI_EVENT_MMU_DERR: + extract_info_from_fw = true; break; case GAUDI_EVENT_TPC0_SERR ... GAUDI_EVENT_TPC7_SERR: index = event_type - GAUDI_EVENT_TPC0_SERR; - block_address = mmTPC0_CFG_BASE + index * TPC_CFG_OFFSET; - snprintf(desc, ARRAY_SIZE(desc), "%s%d", "TPC", index); - num_memories = 90; - derr = false; - disable_clock_gating = true; + params.block_address = mmTPC0_CFG_BASE + index * TPC_CFG_OFFSET; + params.num_memories = 90; + params.derr = false; + params.disable_clock_gating = true; + extract_info_from_fw = false; break; case GAUDI_EVENT_TPC0_DERR ... GAUDI_EVENT_TPC7_DERR: index = event_type - GAUDI_EVENT_TPC0_DERR; - block_address = + params.block_address = mmTPC0_CFG_BASE + index * TPC_CFG_OFFSET; - snprintf(desc, ARRAY_SIZE(desc), "%s%d", "TPC", index); - num_memories = 90; - derr = true; - disable_clock_gating = true; + params.num_memories = 90; + params.derr = true; + params.disable_clock_gating = true; + extract_info_from_fw = false; break; case GAUDI_EVENT_MME0_ACC_SERR: case GAUDI_EVENT_MME1_ACC_SERR: case GAUDI_EVENT_MME2_ACC_SERR: case GAUDI_EVENT_MME3_ACC_SERR: index = (event_type - GAUDI_EVENT_MME0_ACC_SERR) / 4; - block_address = mmMME0_ACC_BASE + index * MME_ACC_OFFSET; - snprintf(desc, ARRAY_SIZE(desc), "MME%d_ACC", index); - num_memories = 128; - derr = false; - disable_clock_gating = true; + params.block_address = mmMME0_ACC_BASE + index * MME_ACC_OFFSET; + params.num_memories = 128; + params.derr = false; + params.disable_clock_gating = true; + extract_info_from_fw = false; break; case GAUDI_EVENT_MME0_ACC_DERR: case GAUDI_EVENT_MME1_ACC_DERR: case GAUDI_EVENT_MME2_ACC_DERR: case GAUDI_EVENT_MME3_ACC_DERR: index = (event_type - GAUDI_EVENT_MME0_ACC_DERR) / 4; - block_address = mmMME0_ACC_BASE + index * MME_ACC_OFFSET; - snprintf(desc, ARRAY_SIZE(desc), "MME%d_ACC", index); - num_memories = 128; - derr = true; - disable_clock_gating = true; + params.block_address = mmMME0_ACC_BASE + index * MME_ACC_OFFSET; + params.num_memories = 128; + params.derr = true; + params.disable_clock_gating = true; + extract_info_from_fw = false; break; case GAUDI_EVENT_MME0_SBAB_SERR: case GAUDI_EVENT_MME1_SBAB_SERR: case GAUDI_EVENT_MME2_SBAB_SERR: case GAUDI_EVENT_MME3_SBAB_SERR: index = (event_type - GAUDI_EVENT_MME0_SBAB_SERR) / 4; - block_address = mmMME0_SBAB_BASE + index * MME_ACC_OFFSET; - snprintf(desc, ARRAY_SIZE(desc), "MME%d_SBAB", index); - num_memories = 33; - derr = false; - disable_clock_gating = true; + params.block_address = + mmMME0_SBAB_BASE + index * MME_ACC_OFFSET; + params.num_memories = 33; + params.derr = false; + params.disable_clock_gating = true; + extract_info_from_fw = false; break; case GAUDI_EVENT_MME0_SBAB_DERR: case GAUDI_EVENT_MME1_SBAB_DERR: case GAUDI_EVENT_MME2_SBAB_DERR: case GAUDI_EVENT_MME3_SBAB_DERR: index = (event_type - GAUDI_EVENT_MME0_SBAB_DERR) / 4; - block_address = mmMME0_SBAB_BASE + index * MME_ACC_OFFSET; - snprintf(desc, ARRAY_SIZE(desc), "MME%d_SBAB", index); - num_memories = 33; - derr = true; - disable_clock_gating = true; - break; - case GAUDI_EVENT_DMA0_SERR_ECC ... GAUDI_EVENT_DMA7_SERR_ECC: - index = event_type - GAUDI_EVENT_DMA0_SERR_ECC; - block_address = mmDMA0_CORE_BASE + index * DMA_CORE_OFFSET; - snprintf(desc, ARRAY_SIZE(desc), "DMA%d_CORE", index); - num_memories = 16; - derr = false; - disable_clock_gating = false; - break; - case GAUDI_EVENT_DMA0_DERR_ECC ... GAUDI_EVENT_DMA7_DERR_ECC: - index = event_type - GAUDI_EVENT_DMA0_DERR_ECC; - block_address = mmDMA0_CORE_BASE + index * DMA_CORE_OFFSET; - snprintf(desc, ARRAY_SIZE(desc), "DMA%d_CORE", index); - num_memories = 16; - derr = true; - disable_clock_gating = false; - break; - case GAUDI_EVENT_CPU_IF_ECC_SERR: - block_address = mmCPU_IF_BASE; - snprintf(desc, ARRAY_SIZE(desc), "%s", "CPU"); - num_memories = 4; - derr = false; - disable_clock_gating = false; - break; - case GAUDI_EVENT_CPU_IF_ECC_DERR: - block_address = mmCPU_IF_BASE; - snprintf(desc, ARRAY_SIZE(desc), "%s", "CPU"); - num_memories = 4; - derr = true; - disable_clock_gating = false; - break; - case GAUDI_EVENT_PSOC_MEM_SERR: - block_address = mmPSOC_GLOBAL_CONF_BASE; - snprintf(desc, ARRAY_SIZE(desc), "%s", "CPU"); - num_memories = 4; - derr = false; - disable_clock_gating = false; - break; - case GAUDI_EVENT_PSOC_MEM_DERR: - block_address = mmPSOC_GLOBAL_CONF_BASE; - snprintf(desc, ARRAY_SIZE(desc), "%s", "CPU"); - num_memories = 4; - derr = true; - disable_clock_gating = false; - break; - case GAUDI_EVENT_PSOC_CORESIGHT_SERR: - block_address = mmPSOC_CS_TRACE_BASE; - snprintf(desc, ARRAY_SIZE(desc), "%s", "CPU"); - num_memories = 2; - derr = false; - disable_clock_gating = false; - break; - case GAUDI_EVENT_PSOC_CORESIGHT_DERR: - block_address = mmPSOC_CS_TRACE_BASE; - snprintf(desc, ARRAY_SIZE(desc), "%s", "CPU"); - num_memories = 2; - derr = true; - disable_clock_gating = false; - break; - case GAUDI_EVENT_SRAM0_SERR ... GAUDI_EVENT_SRAM28_SERR: - index = event_type - GAUDI_EVENT_SRAM0_SERR; - block_address = - mmSRAM_Y0_X0_BANK_BASE + index * SRAM_BANK_OFFSET; - snprintf(desc, ARRAY_SIZE(desc), "SRAM%d", index); - num_memories = 2; - derr = false; - disable_clock_gating = false; - break; - case GAUDI_EVENT_SRAM0_DERR ... GAUDI_EVENT_SRAM28_DERR: - index = event_type - GAUDI_EVENT_SRAM0_DERR; - block_address = - mmSRAM_Y0_X0_BANK_BASE + index * SRAM_BANK_OFFSET; - snprintf(desc, ARRAY_SIZE(desc), "SRAM%d", index); - num_memories = 2; - derr = true; - disable_clock_gating = false; - break; - case GAUDI_EVENT_DMA_IF0_SERR ... GAUDI_EVENT_DMA_IF3_SERR: - index = event_type - GAUDI_EVENT_DMA_IF0_SERR; - block_address = mmDMA_IF_W_S_BASE + - index * (mmDMA_IF_E_S_BASE - mmDMA_IF_W_S_BASE); - snprintf(desc, ARRAY_SIZE(desc), "DMA_IF%d", index); - num_memories = 60; - derr = false; - disable_clock_gating = false; - break; - case GAUDI_EVENT_DMA_IF0_DERR ... GAUDI_EVENT_DMA_IF3_DERR: - index = event_type - GAUDI_EVENT_DMA_IF0_DERR; - block_address = mmDMA_IF_W_S_BASE + - index * (mmDMA_IF_E_S_BASE - mmDMA_IF_W_S_BASE); - snprintf(desc, ARRAY_SIZE(desc), "DMA_IF%d", index); - derr = true; - num_memories = 60; - disable_clock_gating = false; - break; - case GAUDI_EVENT_HBM_0_SERR ... GAUDI_EVENT_HBM_3_SERR: - index = event_type - GAUDI_EVENT_HBM_0_SERR; - /* HBM Registers are at different offsets */ - block_address = mmHBM0_BASE + 0x8000 + - index * (mmHBM1_BASE - mmHBM0_BASE); - snprintf(desc, ARRAY_SIZE(desc), "HBM%d", index); - derr = false; - num_memories = 64; - disable_clock_gating = false; - break; - case GAUDI_EVENT_HBM_0_DERR ... GAUDI_EVENT_HBM_3_DERR: - index = event_type - GAUDI_EVENT_HBM_0_SERR; - /* HBM Registers are at different offsets */ - block_address = mmHBM0_BASE + 0x8000 + - index * (mmHBM1_BASE - mmHBM0_BASE); - snprintf(desc, ARRAY_SIZE(desc), "HBM%d", index); - derr = true; - num_memories = 64; - disable_clock_gating = false; - break; + params.block_address = + mmMME0_SBAB_BASE + index * MME_ACC_OFFSET; + params.num_memories = 33; + params.derr = true; + params.disable_clock_gating = true; default: return; } - gaudi_print_ecc_info_generic(hdev, desc, block_address, num_memories, - derr, disable_clock_gating); + if (extract_info_from_fw) { + ecc_address = le64_to_cpu(ecc_data->ecc_address); + ecc_syndrom = le64_to_cpu(ecc_data->ecc_syndrom); + memory_wrapper_idx = ecc_data->memory_wrapper_idx; + } else { + rc = gaudi_extract_ecc_info(hdev, ¶ms, &ecc_address, + &ecc_syndrom, &memory_wrapper_idx); + if (rc) + return; + } + + dev_err(hdev->dev, + "ECC error detected. address: %#llx. Syndrom: %#llx. block id %u\n", + ecc_address, ecc_syndrom, memory_wrapper_idx); } static void gaudi_handle_qman_err(struct hl_device *hdev, u16 event_type) @@ -5507,8 +5387,6 @@ static void gaudi_print_irq_info(struct hl_device *hdev, u16 event_type, dev_err_ratelimited(hdev->dev, "Received H/W interrupt %d [\"%s\"]\n", event_type, desc); - gaudi_print_ecc_info(hdev, event_type); - if (razwi) { gaudi_print_razwi_info(hdev); gaudi_print_mmu_error_info(hdev); @@ -5738,10 +5616,15 @@ static void gaudi_handle_eqe(struct hl_device *hdev, case GAUDI_EVENT_PSOC_CORESIGHT_DERR: case GAUDI_EVENT_SRAM0_DERR ... GAUDI_EVENT_SRAM28_DERR: case GAUDI_EVENT_DMA_IF0_DERR ... GAUDI_EVENT_DMA_IF3_DERR: - fallthrough; - case GAUDI_EVENT_GIC500: case GAUDI_EVENT_HBM_0_DERR ... GAUDI_EVENT_HBM_3_DERR: case GAUDI_EVENT_MMU_DERR: + gaudi_print_irq_info(hdev, event_type, true); + gaudi_handle_ecc_event(hdev, event_type, &eq_entry->ecc_data); + if (hdev->hard_reset_on_fw_events) + hl_device_reset(hdev, true, false); + break; + + case GAUDI_EVENT_GIC500: case GAUDI_EVENT_AXI_ECC: case GAUDI_EVENT_L2_RAM_ECC: case GAUDI_EVENT_PLL0 ... GAUDI_EVENT_PLL17: @@ -5837,6 +5720,11 @@ static void gaudi_handle_eqe(struct hl_device *hdev, case GAUDI_EVENT_HBM_0_SERR ... GAUDI_EVENT_HBM_3_SERR: fallthrough; case GAUDI_EVENT_MMU_SERR: + gaudi_print_irq_info(hdev, event_type, true); + gaudi_handle_ecc_event(hdev, event_type, &eq_entry->ecc_data); + hl_fw_unmask_irq(hdev, event_type); + break; + case GAUDI_EVENT_PCIE_DEC: case GAUDI_EVENT_MME0_WBC_RSP: case GAUDI_EVENT_MME0_SBAB0_RSP: diff --git a/drivers/misc/habanalabs/include/armcp_if.h b/drivers/misc/habanalabs/include/armcp_if.h index dea7c90faafa..07f9972db28d 100644 --- a/drivers/misc/habanalabs/include/armcp_if.h +++ b/drivers/misc/habanalabs/include/armcp_if.h @@ -19,9 +19,19 @@ struct hl_eq_header { __le32 ctl; }; +struct hl_eq_ecc_data { + __le64 ecc_address; + __le64 ecc_syndrom; + __u8 memory_wrapper_idx; + __u8 pad[7]; +}; + struct hl_eq_entry { struct hl_eq_header hdr; - __le64 data[7]; + union { + struct hl_eq_ecc_data ecc_data; + __le64 data[7]; + }; }; #define HL_EQ_ENTRY_SIZE sizeof(struct hl_eq_entry) diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/gaudi_regs.h b/drivers/misc/habanalabs/include/gaudi/asic_reg/gaudi_regs.h index 62078077aee5..0c75d43532bd 100644 --- a/drivers/misc/habanalabs/include/gaudi/asic_reg/gaudi_regs.h +++ b/drivers/misc/habanalabs/include/gaudi/asic_reg/gaudi_regs.h @@ -93,17 +93,14 @@ #include "psoc_hbm_pll_regs.h" #include "psoc_cpu_pll_regs.h" -#define GAUDI_ECC_MEM_SEL_OFFSET 0xF18 -#define GAUDI_ECC_ADDRESS_OFFSET 0xF1C -#define GAUDI_ECC_SYNDROME_OFFSET 0xF20 -#define GAUDI_ECC_SERR0_OFFSET 0xF30 -#define GAUDI_ECC_SERR1_OFFSET 0xF34 -#define GAUDI_ECC_SERR2_OFFSET 0xF38 -#define GAUDI_ECC_SERR3_OFFSET 0xF3C -#define GAUDI_ECC_DERR0_OFFSET 0xF40 -#define GAUDI_ECC_DERR1_OFFSET 0xF44 -#define GAUDI_ECC_DERR2_OFFSET 0xF48 -#define GAUDI_ECC_DERR3_OFFSET 0xF4C +#define GAUDI_ECC_MEM_SEL_OFFSET 0xF18 +#define GAUDI_ECC_ADDRESS_OFFSET 0xF1C +#define GAUDI_ECC_SYNDROME_OFFSET 0xF20 +#define GAUDI_ECC_MEM_INFO_CLR_OFFSET 0xF28 +#define GAUDI_ECC_MEM_INFO_CLR_SERR_MASK BIT(8) +#define GAUDI_ECC_MEM_INFO_CLR_DERR_MASK BIT(9) +#define GAUDI_ECC_SERR0_OFFSET 0xF30 +#define GAUDI_ECC_DERR0_OFFSET 0xF40 #define mmSYNC_MNGR_W_S_SYNC_MNGR_OBJS_SOB_OBJ_0 0x492000 #define mmSYNC_MNGR_W_S_SYNC_MNGR_OBJS_MON_PAY_ADDRL_0 0x494000 -- cgit v1.2.3-59-g8ed1b From f4cbfd2445ffa3415023899e43ac9c8334171cf4 Mon Sep 17 00:00:00 2001 From: Ofir Bitton Date: Mon, 15 Jun 2020 17:45:12 +0300 Subject: habanalabs: PCIe iATU refactoring Divide iATU initialization into inbound/outbound methods. We must separate it in order to enable different match mode per PCIe region. In addition, added support for PCI address match mode. Signed-off-by: Ofir Bitton Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/gaudi/gaudi.c | 59 +++++++++------ drivers/misc/habanalabs/goya/goya.c | 37 ++++++++- drivers/misc/habanalabs/habanalabs.h | 52 +++++++++++-- drivers/misc/habanalabs/pci.c | 136 ++++++++++++++++------------------ 4 files changed, 180 insertions(+), 104 deletions(-) diff --git a/drivers/misc/habanalabs/gaudi/gaudi.c b/drivers/misc/habanalabs/gaudi/gaudi.c index 888f42adee6a..a6e40dec3cd2 100644 --- a/drivers/misc/habanalabs/gaudi/gaudi.c +++ b/drivers/misc/habanalabs/gaudi/gaudi.c @@ -465,6 +465,7 @@ static int gaudi_pci_bars_map(struct hl_device *hdev) static u64 gaudi_set_hbm_bar_base(struct hl_device *hdev, u64 addr) { struct gaudi_device *gaudi = hdev->asic_specific; + struct hl_inbound_pci_region pci_region; u64 old_addr = addr; int rc; @@ -472,7 +473,10 @@ static u64 gaudi_set_hbm_bar_base(struct hl_device *hdev, u64 addr) return old_addr; /* Inbound Region 2 - Bar 4 - Point to HBM */ - rc = hl_pci_set_dram_bar_base(hdev, 2, 4, addr); + pci_region.mode = PCI_BAR_MATCH_MODE; + pci_region.bar = HBM_BAR_ID; + pci_region.addr = addr; + rc = hl_pci_set_inbound_region(hdev, 2, &pci_region); if (rc) return U64_MAX; @@ -486,22 +490,43 @@ static u64 gaudi_set_hbm_bar_base(struct hl_device *hdev, u64 addr) static int gaudi_init_iatu(struct hl_device *hdev) { - int rc = 0; + struct hl_inbound_pci_region inbound_region; + struct hl_outbound_pci_region outbound_region; + int rc; + + /* Inbound Region 0 - Bar 0 - Point to SRAM + CFG */ + inbound_region.mode = PCI_BAR_MATCH_MODE; + inbound_region.bar = SRAM_BAR_ID; + inbound_region.addr = SRAM_BASE_ADDR; + rc = hl_pci_set_inbound_region(hdev, 0, &inbound_region); + if (rc) + goto done; /* Inbound Region 1 - Bar 2 - Point to SPI FLASH */ - rc = hl_pci_iatu_write(hdev, 0x314, - lower_32_bits(SPI_FLASH_BASE_ADDR)); - rc |= hl_pci_iatu_write(hdev, 0x318, - upper_32_bits(SPI_FLASH_BASE_ADDR)); - rc |= hl_pci_iatu_write(hdev, 0x300, 0); - /* Enable + Bar match + match enable */ - rc |= hl_pci_iatu_write(hdev, 0x304, 0xC0080200); + inbound_region.mode = PCI_BAR_MATCH_MODE; + inbound_region.bar = CFG_BAR_ID; + inbound_region.addr = SPI_FLASH_BASE_ADDR; + rc = hl_pci_set_inbound_region(hdev, 1, &inbound_region); + if (rc) + goto done; + /* Inbound Region 2 - Bar 4 - Point to HBM */ + inbound_region.mode = PCI_BAR_MATCH_MODE; + inbound_region.bar = HBM_BAR_ID; + inbound_region.addr = DRAM_PHYS_BASE; + rc = hl_pci_set_inbound_region(hdev, 2, &inbound_region); if (rc) - return -EIO; + goto done; + + hdev->asic_funcs->set_dma_mask_from_fw(hdev); - return hl_pci_init_iatu(hdev, SRAM_BASE_ADDR, DRAM_PHYS_BASE, - HOST_PHYS_BASE, HOST_PHYS_SIZE); + /* Outbound Region 0 - Point to Host */ + outbound_region.addr = HOST_PHYS_BASE; + outbound_region.size = HOST_PHYS_SIZE; + rc = hl_pci_set_outbound_region(hdev, &outbound_region); + +done: + return rc; } static int gaudi_early_init(struct hl_device *hdev) @@ -2884,16 +2909,6 @@ static int gaudi_hw_init(struct hl_device *hdev) gaudi_init_hbm_dma_qmans(hdev); - /* - * Before pushing u-boot/linux to device, need to set the hbm bar to - * base address of dram - */ - if (gaudi_set_hbm_bar_base(hdev, DRAM_PHYS_BASE) == U64_MAX) { - dev_err(hdev->dev, - "failed to map HBM bar to DRAM base address\n"); - return -EIO; - } - rc = gaudi_init_cpu(hdev); if (rc) { dev_err(hdev->dev, "failed to initialize CPU\n"); diff --git a/drivers/misc/habanalabs/goya/goya.c b/drivers/misc/habanalabs/goya/goya.c index ff32a8fa7624..5839b5bc9ee3 100644 --- a/drivers/misc/habanalabs/goya/goya.c +++ b/drivers/misc/habanalabs/goya/goya.c @@ -458,6 +458,7 @@ static int goya_pci_bars_map(struct hl_device *hdev) static u64 goya_set_ddr_bar_base(struct hl_device *hdev, u64 addr) { struct goya_device *goya = hdev->asic_specific; + struct hl_inbound_pci_region pci_region; u64 old_addr = addr; int rc; @@ -465,7 +466,10 @@ static u64 goya_set_ddr_bar_base(struct hl_device *hdev, u64 addr) return old_addr; /* Inbound Region 1 - Bar 4 - Point to DDR */ - rc = hl_pci_set_dram_bar_base(hdev, 1, 4, addr); + pci_region.mode = PCI_BAR_MATCH_MODE; + pci_region.bar = DDR_BAR_ID; + pci_region.addr = addr; + rc = hl_pci_set_inbound_region(hdev, 1, &pci_region); if (rc) return U64_MAX; @@ -487,8 +491,35 @@ static u64 goya_set_ddr_bar_base(struct hl_device *hdev, u64 addr) */ static int goya_init_iatu(struct hl_device *hdev) { - return hl_pci_init_iatu(hdev, SRAM_BASE_ADDR, DRAM_PHYS_BASE, - HOST_PHYS_BASE, HOST_PHYS_SIZE); + struct hl_inbound_pci_region inbound_region; + struct hl_outbound_pci_region outbound_region; + int rc; + + /* Inbound Region 0 - Bar 0 - Point to SRAM and CFG */ + inbound_region.mode = PCI_BAR_MATCH_MODE; + inbound_region.bar = SRAM_CFG_BAR_ID; + inbound_region.addr = SRAM_BASE_ADDR; + rc = hl_pci_set_inbound_region(hdev, 0, &inbound_region); + if (rc) + goto done; + + /* Inbound Region 1 - Bar 4 - Point to DDR */ + inbound_region.mode = PCI_BAR_MATCH_MODE; + inbound_region.bar = DDR_BAR_ID; + inbound_region.addr = DRAM_PHYS_BASE; + rc = hl_pci_set_inbound_region(hdev, 1, &inbound_region); + if (rc) + goto done; + + hdev->asic_funcs->set_dma_mask_from_fw(hdev); + + /* Outbound Region 0 - Point to Host */ + outbound_region.addr = HOST_PHYS_BASE; + outbound_region.size = HOST_PHYS_SIZE; + rc = hl_pci_set_outbound_region(hdev, &outbound_region); + +done: + return rc; } /* diff --git a/drivers/misc/habanalabs/habanalabs.h b/drivers/misc/habanalabs/habanalabs.h index ae781453a509..365236589bbf 100644 --- a/drivers/misc/habanalabs/habanalabs.h +++ b/drivers/misc/habanalabs/habanalabs.h @@ -66,6 +66,8 @@ #define IS_POWER_OF_2(n) (n != 0 && ((n & (n - 1)) == 0)) #define IS_MAX_PENDING_CS_VALID(n) (IS_POWER_OF_2(n) && (n > 1)) +#define HL_PCI_NUM_BARS 6 + /** * struct pgt_info - MMU hop page info. * @node: hash linked-list node for the pgts shadow hash of pgts. @@ -90,6 +92,16 @@ struct pgt_info { struct hl_device; struct hl_fpriv; +/** + * enum hl_pci_match_mode - pci match mode per region + * @PCI_ADDRESS_MATCH_MODE: address match mode + * @PCI_BAR_MATCH_MODE: bar match mode + */ +enum hl_pci_match_mode { + PCI_ADDRESS_MATCH_MODE, + PCI_BAR_MATCH_MODE +}; + /** * enum hl_fw_component - F/W components to read version through registers. * @FW_COMP_UBOOT: u-boot. @@ -125,6 +137,32 @@ enum hl_cs_type { CS_TYPE_WAIT }; +/* + * struct hl_inbound_pci_region - inbound region descriptor + * @mode: pci match mode for this region + * @addr: region target address + * @size: region size in bytes + * @offset_in_bar: offset within bar (address match mode) + * @bar: bar id + */ +struct hl_inbound_pci_region { + enum hl_pci_match_mode mode; + u64 addr; + u64 size; + u64 offset_in_bar; + u8 bar; +}; + +/* + * struct hl_outbound_pci_region - outbound region descriptor + * @addr: region target address + * @size: region size in bytes + */ +struct hl_outbound_pci_region { + u64 addr; + u64 size; +}; + /* * struct hl_hw_sob - H/W SOB info. * @hdev: habanalabs device structure. @@ -1347,7 +1385,9 @@ struct hl_device_idle_busy_ts { /** * struct hl_device - habanalabs device structure. * @pdev: pointer to PCI device, can be NULL in case of simulator device. - * @pcie_bar: array of available PCIe bars. + * @pcie_bar_phys: array of available PCIe bars physical addresses. + * (required only for PCI address match mode) + * @pcie_bar: array of available PCIe bars virtual addresses. * @rmmio: configuration area address on SRAM. * @cdev: related char device. * @cdev_ctrl: char device for control operations only (INFO IOCTL) @@ -1442,7 +1482,8 @@ struct hl_device_idle_busy_ts { */ struct hl_device { struct pci_dev *pdev; - void __iomem *pcie_bar[6]; + u64 pcie_bar_phys[HL_PCI_NUM_BARS]; + void __iomem *pcie_bar[HL_PCI_NUM_BARS]; void __iomem *rmmio; struct cdev cdev; struct cdev cdev_ctrl; @@ -1767,9 +1808,10 @@ int hl_pci_bars_map(struct hl_device *hdev, const char * const name[3], int hl_pci_iatu_write(struct hl_device *hdev, u32 addr, u32 data); int hl_pci_set_dram_bar_base(struct hl_device *hdev, u8 inbound_region, u8 bar, u64 addr); -int hl_pci_init_iatu(struct hl_device *hdev, u64 sram_base_address, - u64 dram_base_address, u64 host_phys_base_address, - u64 host_phys_size); +int hl_pci_set_inbound_region(struct hl_device *hdev, u8 region, + struct hl_inbound_pci_region *pci_region); +int hl_pci_set_outbound_region(struct hl_device *hdev, + struct hl_outbound_pci_region *pci_region); int hl_pci_init(struct hl_device *hdev); void hl_pci_fini(struct hl_device *hdev); diff --git a/drivers/misc/habanalabs/pci.c b/drivers/misc/habanalabs/pci.c index 61a8bb07262c..1791f6623c69 100644 --- a/drivers/misc/habanalabs/pci.c +++ b/drivers/misc/habanalabs/pci.c @@ -9,9 +9,15 @@ #include "include/hw_ip/pci/pci_general.h" #include +#include #define HL_PLDM_PCI_ELBI_TIMEOUT_MSEC (HL_PCI_ELBI_TIMEOUT_MSEC * 10) +#define IATU_REGION_CTRL_REGION_EN_MASK BIT(31) +#define IATU_REGION_CTRL_MATCH_MODE_MASK BIT(30) +#define IATU_REGION_CTRL_NUM_MATCH_EN_MASK BIT(19) +#define IATU_REGION_CTRL_BAR_NUM_MASK GENMASK(10, 8) + /** * hl_pci_bars_map() - Map PCI BARs. * @hdev: Pointer to hl_device structure. @@ -187,110 +193,94 @@ static void hl_pci_reset_link_through_bridge(struct hl_device *hdev) } /** - * hl_pci_set_dram_bar_base() - Set DDR BAR to map specific device address. + * hl_pci_set_inbound_region() - Configure inbound region * @hdev: Pointer to hl_device structure. - * @inbound_region: Inbound region number. - * @bar: PCI BAR number. - * @addr: Address in DRAM. Must be aligned to DRAM bar size. + * @region: Inbound region number. + * @pci_region: Inbound region parameters. * - * Configure the iATU so that the DRAM bar will start at the specified address. + * Configure the iATU inbound region. * * Return: 0 on success, negative value for failure. */ -int hl_pci_set_dram_bar_base(struct hl_device *hdev, u8 inbound_region, u8 bar, - u64 addr) +int hl_pci_set_inbound_region(struct hl_device *hdev, u8 region, + struct hl_inbound_pci_region *pci_region) { struct asic_fixed_properties *prop = &hdev->asic_prop; - u32 offset; - int rc; + u64 bar_phys_base, region_base, region_end_address; + u32 offset, ctrl_reg_val; + int rc = 0; - switch (inbound_region) { - case 0: - offset = 0x100; - break; - case 1: - offset = 0x300; - break; - case 2: - offset = 0x500; - break; - default: - dev_err(hdev->dev, "Invalid inbound region %d\n", - inbound_region); - return -EINVAL; - } + /* region offset */ + offset = (0x200 * region) + 0x100; + + if (pci_region->mode == PCI_ADDRESS_MATCH_MODE) { + bar_phys_base = hdev->pcie_bar_phys[pci_region->bar]; + region_base = bar_phys_base + pci_region->offset_in_bar; + region_end_address = region_base + pci_region->size - 1; - if (bar != 0 && bar != 2 && bar != 4) { - dev_err(hdev->dev, "Invalid PCI BAR %d\n", bar); - return -EINVAL; + rc |= hl_pci_iatu_write(hdev, offset + 0x8, + lower_32_bits(region_base)); + rc |= hl_pci_iatu_write(hdev, offset + 0xC, + upper_32_bits(region_base)); + rc |= hl_pci_iatu_write(hdev, offset + 0x10, + lower_32_bits(region_end_address)); } /* Point to the specified address */ - rc = hl_pci_iatu_write(hdev, offset + 0x14, lower_32_bits(addr)); - rc |= hl_pci_iatu_write(hdev, offset + 0x18, upper_32_bits(addr)); + rc = hl_pci_iatu_write(hdev, offset + 0x14, + lower_32_bits(pci_region->addr)); + rc |= hl_pci_iatu_write(hdev, offset + 0x18, + upper_32_bits(pci_region->addr)); rc |= hl_pci_iatu_write(hdev, offset + 0x0, 0); - /* Enable + BAR match + match enable + BAR number */ - rc |= hl_pci_iatu_write(hdev, offset + 0x4, 0xC0080000 | (bar << 8)); + + /* Enable + bar/address match + match enable + bar number */ + ctrl_reg_val = FIELD_PREP(IATU_REGION_CTRL_REGION_EN_MASK, 1); + ctrl_reg_val |= FIELD_PREP(IATU_REGION_CTRL_MATCH_MODE_MASK, + pci_region->mode); + ctrl_reg_val |= FIELD_PREP(IATU_REGION_CTRL_NUM_MATCH_EN_MASK, 1); + + if (pci_region->mode == PCI_BAR_MATCH_MODE) + ctrl_reg_val |= FIELD_PREP(IATU_REGION_CTRL_BAR_NUM_MASK, + pci_region->bar); + + rc |= hl_pci_iatu_write(hdev, offset + 0x4, ctrl_reg_val); /* Return the DBI window to the default location */ rc |= hl_pci_elbi_write(hdev, prop->pcie_aux_dbi_reg_addr, 0); rc |= hl_pci_elbi_write(hdev, prop->pcie_aux_dbi_reg_addr + 4, 0); if (rc) - dev_err(hdev->dev, "failed to map DRAM bar to 0x%08llx\n", - addr); + dev_err(hdev->dev, "failed to map bar %u to 0x%08llx\n", + pci_region->bar, pci_region->addr); return rc; } /** - * hl_pci_init_iatu() - Initialize the iATU unit inside the PCI controller. + * hl_pci_set_outbound_region() - Configure outbound region 0 * @hdev: Pointer to hl_device structure. - * @sram_base_address: SRAM base address. - * @dram_base_address: DRAM base address. - * @host_phys_base_address: Base physical address of host memory for device - * transactions. - * @host_phys_size: Size of host memory for device transactions. + * @pci_region: Outbound region parameters. * - * This is needed in case the firmware doesn't initialize the iATU. + * Configure the iATU outbound region 0. * * Return: 0 on success, negative value for failure. */ -int hl_pci_init_iatu(struct hl_device *hdev, u64 sram_base_address, - u64 dram_base_address, u64 host_phys_base_address, - u64 host_phys_size) +int hl_pci_set_outbound_region(struct hl_device *hdev, + struct hl_outbound_pci_region *pci_region) { struct asic_fixed_properties *prop = &hdev->asic_prop; - u64 host_phys_end_addr; + u64 outbound_region_end_address; int rc = 0; - /* Inbound Region 0 - Bar 0 - Point to SRAM base address */ - rc = hl_pci_iatu_write(hdev, 0x114, lower_32_bits(sram_base_address)); - rc |= hl_pci_iatu_write(hdev, 0x118, upper_32_bits(sram_base_address)); - rc |= hl_pci_iatu_write(hdev, 0x100, 0); - /* Enable + Bar match + match enable */ - rc |= hl_pci_iatu_write(hdev, 0x104, 0xC0080000); - - /* Return the DBI window to the default location */ - rc |= hl_pci_elbi_write(hdev, prop->pcie_aux_dbi_reg_addr, 0); - rc |= hl_pci_elbi_write(hdev, prop->pcie_aux_dbi_reg_addr + 4, 0); - - hdev->asic_funcs->set_dma_mask_from_fw(hdev); - - /* Point to DRAM */ - if (!hdev->asic_funcs->set_dram_bar_base) - return -EINVAL; - if (hdev->asic_funcs->set_dram_bar_base(hdev, dram_base_address) == - U64_MAX) - return -EIO; - - /* Outbound Region 0 - Point to Host */ - host_phys_end_addr = host_phys_base_address + host_phys_size - 1; + /* Outbound Region 0 */ + outbound_region_end_address = + pci_region->addr + pci_region->size - 1; rc |= hl_pci_iatu_write(hdev, 0x008, - lower_32_bits(host_phys_base_address)); + lower_32_bits(pci_region->addr)); rc |= hl_pci_iatu_write(hdev, 0x00C, - upper_32_bits(host_phys_base_address)); - rc |= hl_pci_iatu_write(hdev, 0x010, lower_32_bits(host_phys_end_addr)); + upper_32_bits(pci_region->addr)); + rc |= hl_pci_iatu_write(hdev, 0x010, + lower_32_bits(outbound_region_end_address)); rc |= hl_pci_iatu_write(hdev, 0x014, 0); if ((hdev->power9_64bit_dma_enable) && (hdev->dma_mask == 64)) @@ -298,7 +288,8 @@ int hl_pci_init_iatu(struct hl_device *hdev, u64 sram_base_address, else rc |= hl_pci_iatu_write(hdev, 0x018, 0); - rc |= hl_pci_iatu_write(hdev, 0x020, upper_32_bits(host_phys_end_addr)); + rc |= hl_pci_iatu_write(hdev, 0x020, + upper_32_bits(outbound_region_end_address)); /* Increase region size */ rc |= hl_pci_iatu_write(hdev, 0x000, 0x00002000); /* Enable */ @@ -308,10 +299,7 @@ int hl_pci_init_iatu(struct hl_device *hdev, u64 sram_base_address, rc |= hl_pci_elbi_write(hdev, prop->pcie_aux_dbi_reg_addr, 0); rc |= hl_pci_elbi_write(hdev, prop->pcie_aux_dbi_reg_addr + 4, 0); - if (rc) - return -EIO; - - return 0; + return rc; } /** -- cgit v1.2.3-59-g8ed1b From 12ae3133d2df4d5091cbf6a966a98f5e07c31b56 Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Fri, 3 Jul 2020 20:58:23 +0300 Subject: habanalabs: remove soft-reset support from GAUDI Soft-reset isn't supported in GAUDI. Remove the code that performs it and print error in case the user wants to do it via sysfs. Signed-off-by: Oded Gabbay Reviewed-by: Tomer Tayar --- drivers/misc/habanalabs/gaudi/gaudi.c | 99 +++++++++++++---------------------- 1 file changed, 36 insertions(+), 63 deletions(-) diff --git a/drivers/misc/habanalabs/gaudi/gaudi.c b/drivers/misc/habanalabs/gaudi/gaudi.c index a6e40dec3cd2..eede6c33a37f 100644 --- a/drivers/misc/habanalabs/gaudi/gaudi.c +++ b/drivers/misc/habanalabs/gaudi/gaudi.c @@ -75,7 +75,6 @@ #define GAUDI_PLDM_RESET_WAIT_MSEC 1000 /* 1s */ #define GAUDI_PLDM_HRESET_TIMEOUT_MSEC 20000 /* 20s */ -#define GAUDI_PLDM_SRESET_TIMEOUT_MSEC 14000 /* 14s */ #define GAUDI_PLDM_TEST_QUEUE_WAIT_USEC 1000000 /* 1s */ #define GAUDI_PLDM_MMU_TIMEOUT_USEC (MMU_CONFIG_TIMEOUT_USEC * 100) #define GAUDI_PLDM_QMAN0_TIMEOUT_USEC (HL_DEVICE_TIMEOUT_USEC * 30) @@ -2587,16 +2586,14 @@ static void gaudi_halt_engines(struct hl_device *hdev, bool hard_reset) cpu_timeout_ms = GAUDI_CPU_RESET_WAIT_MSEC; } - if (hard_reset) { - /* - * I don't know what is the state of the CPU so make sure it is - * stopped in any means necessary - */ - WREG32(mmPSOC_GLOBAL_CONF_KMD_MSG_TO_CPU, KMD_MSG_GOTO_WFE); - WREG32(mmGIC_DISTRIBUTOR__5_GICD_SETSPI_NSR, - GAUDI_EVENT_HALT_MACHINE); - msleep(cpu_timeout_ms); - } + /* + * I don't know what is the state of the CPU so make sure it is + * stopped in any means necessary + */ + WREG32(mmPSOC_GLOBAL_CONF_KMD_MSG_TO_CPU, KMD_MSG_GOTO_WFE); + WREG32(mmGIC_DISTRIBUTOR__5_GICD_SETSPI_NSR, + GAUDI_EVENT_HALT_MACHINE); + msleep(cpu_timeout_ms); gaudi_stop_mme_qmans(hdev); gaudi_stop_tpc_qmans(hdev); @@ -2621,10 +2618,7 @@ static void gaudi_halt_engines(struct hl_device *hdev, bool hard_reset) gaudi_disable_timestamp(hdev); - if (hard_reset) - gaudi_disable_msi(hdev); - else - gaudi_sync_irqs(hdev); + gaudi_disable_msi(hdev); } static int gaudi_mmu_init(struct hl_device *hdev) @@ -2969,46 +2963,37 @@ static void gaudi_hw_fini(struct hl_device *hdev, bool hard_reset) struct gaudi_device *gaudi = hdev->asic_specific; u32 status, reset_timeout_ms, boot_strap = 0; - if (hdev->pldm) { - if (hard_reset) - reset_timeout_ms = GAUDI_PLDM_HRESET_TIMEOUT_MSEC; - else - reset_timeout_ms = GAUDI_PLDM_SRESET_TIMEOUT_MSEC; - } else { - reset_timeout_ms = GAUDI_RESET_TIMEOUT_MSEC; + if (!hard_reset) { + dev_err(hdev->dev, "GAUDI doesn't support soft-reset\n"); + return; } - if (hard_reset) { - /* Tell ASIC not to re-initialize PCIe */ - WREG32(mmPREBOOT_PCIE_EN, LKD_HARD_RESET_MAGIC); + if (hdev->pldm) + reset_timeout_ms = GAUDI_PLDM_HRESET_TIMEOUT_MSEC; + else + reset_timeout_ms = GAUDI_RESET_TIMEOUT_MSEC; + + /* Tell ASIC not to re-initialize PCIe */ + WREG32(mmPREBOOT_PCIE_EN, LKD_HARD_RESET_MAGIC); - boot_strap = RREG32(mmPSOC_GLOBAL_CONF_BOOT_STRAP_PINS); - /* H/W bug WA: - * rdata[31:0] = strap_read_val; - * wdata[31:0] = rdata[30:21],1'b0,rdata[20:0] - */ - boot_strap = (((boot_strap & 0x7FE00000) << 1) | - (boot_strap & 0x001FFFFF)); - WREG32(mmPSOC_GLOBAL_CONF_BOOT_STRAP_PINS, boot_strap & ~0x2); - - /* Restart BTL/BLR upon hard-reset */ - WREG32(mmPSOC_GLOBAL_CONF_BOOT_SEQ_RE_START, 1); - - WREG32(mmPSOC_GLOBAL_CONF_SW_ALL_RST, - 1 << PSOC_GLOBAL_CONF_SW_ALL_RST_IND_SHIFT); - dev_info(hdev->dev, - "Issued HARD reset command, going to wait %dms\n", - reset_timeout_ms); - } else { - /* Don't restart BTL/BLR upon soft-reset */ - WREG32(mmPSOC_GLOBAL_CONF_BOOT_SEQ_RE_START, 0); + boot_strap = RREG32(mmPSOC_GLOBAL_CONF_BOOT_STRAP_PINS); - WREG32(mmPSOC_GLOBAL_CONF_SOFT_RST, - 1 << PSOC_GLOBAL_CONF_SOFT_RST_IND_SHIFT); - dev_info(hdev->dev, - "Issued SOFT reset command, going to wait %dms\n", - reset_timeout_ms); - } + /* H/W bug WA: + * rdata[31:0] = strap_read_val; + * wdata[31:0] = rdata[30:21],1'b0,rdata[20:0] + */ + boot_strap = (((boot_strap & 0x7FE00000) << 1) | + (boot_strap & 0x001FFFFF)); + WREG32(mmPSOC_GLOBAL_CONF_BOOT_STRAP_PINS, boot_strap & ~0x2); + + /* Restart BTL/BLR upon hard-reset */ + WREG32(mmPSOC_GLOBAL_CONF_BOOT_SEQ_RE_START, 1); + + WREG32(mmPSOC_GLOBAL_CONF_SW_ALL_RST, + 1 << PSOC_GLOBAL_CONF_SW_ALL_RST_IND_SHIFT); + dev_info(hdev->dev, + "Issued HARD reset command, going to wait %dms\n", + reset_timeout_ms); /* * After hard reset, we can't poll the BTM_FSM register because the PSOC @@ -3022,18 +3007,6 @@ static void gaudi_hw_fini(struct hl_device *hdev, bool hard_reset) "Timeout while waiting for device to reset 0x%x\n", status); - if (!hard_reset) { - gaudi->hw_cap_initialized &= ~(HW_CAP_PCI_DMA | HW_CAP_MME | - HW_CAP_TPC_MASK | - HW_CAP_HBM_DMA); - - WREG32(mmGIC_DISTRIBUTOR__5_GICD_SETSPI_NSR, - GAUDI_EVENT_SOFT_RESET); - return; - } - - /* We continue here only for hard-reset */ - WREG32(mmPSOC_GLOBAL_CONF_BOOT_STRAP_PINS, boot_strap); gaudi->hw_cap_initialized &= ~(HW_CAP_CPU | HW_CAP_CPU_Q | -- cgit v1.2.3-59-g8ed1b From 3abc99bb7dcbc0704972dae6c6ba92fbb1fbf191 Mon Sep 17 00:00:00 2001 From: Ofir Bitton Date: Tue, 23 Jun 2020 14:50:39 +0300 Subject: habanalabs: configure maximum queues per asic Currently the amount of maximum queues is statically configured. Using a static value is causing redundunt cycles when traversing all queues and consumes more memory than actually needed. In this patch we configure each asic with the exact number of queues needed. Signed-off-by: Ofir Bitton Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/command_submission.c | 24 ++++++++++++++++---- drivers/misc/habanalabs/gaudi/gaudi.c | 33 +++++++++++++++------------ drivers/misc/habanalabs/goya/goya.c | 34 +++++++++++++++++++++------- drivers/misc/habanalabs/goya/goyaP.h | 6 +---- drivers/misc/habanalabs/habanalabs.h | 10 ++++---- drivers/misc/habanalabs/hw_queue.c | 19 ++++++++++------ 6 files changed, 83 insertions(+), 43 deletions(-) diff --git a/drivers/misc/habanalabs/command_submission.c b/drivers/misc/habanalabs/command_submission.c index 777f88d25acd..7769a1aacca1 100644 --- a/drivers/misc/habanalabs/command_submission.c +++ b/drivers/misc/habanalabs/command_submission.c @@ -363,6 +363,7 @@ static void cs_do_release(struct kref *ref) cs_counters_aggregate(hdev, cs->ctx); + kfree(cs->jobs_in_queue_cnt); kfree(cs); } @@ -435,13 +436,19 @@ static int allocate_cs(struct hl_device *hdev, struct hl_ctx *ctx, other = ctx->cs_pending[cs_cmpl->cs_seq & (hdev->asic_prop.max_pending_cs - 1)]; if ((other) && (!dma_fence_is_signaled(other))) { - spin_unlock(&ctx->cs_lock); dev_dbg(hdev->dev, "Rejecting CS because of too many in-flights CS\n"); rc = -EAGAIN; goto free_fence; } + cs->jobs_in_queue_cnt = kcalloc(hdev->asic_prop.max_queues, + sizeof(*cs->jobs_in_queue_cnt), GFP_ATOMIC); + if (!cs->jobs_in_queue_cnt) { + rc = -ENOMEM; + goto free_fence; + } + dma_fence_init(&cs_cmpl->base_fence, &hl_fence_ops, &cs_cmpl->lock, ctx->asid, ctx->cs_sequence); @@ -463,6 +470,7 @@ static int allocate_cs(struct hl_device *hdev, struct hl_ctx *ctx, return 0; free_fence: + spin_unlock(&ctx->cs_lock); kfree(cs_cmpl); free_cs: kfree(cs); @@ -515,10 +523,18 @@ static int validate_queue_index(struct hl_device *hdev, struct asic_fixed_properties *asic = &hdev->asic_prop; struct hw_queue_properties *hw_queue_prop; + /* This must be checked here to prevent out-of-bounds access to + * hw_queues_props array + */ + if (chunk->queue_index >= asic->max_queues) { + dev_err(hdev->dev, "Queue index %d is invalid\n", + chunk->queue_index); + return -EINVAL; + } + hw_queue_prop = &asic->hw_queues_props[chunk->queue_index]; - if ((chunk->queue_index >= HL_MAX_QUEUES) || - (hw_queue_prop->type == QUEUE_TYPE_NA)) { + if (hw_queue_prop->type == QUEUE_TYPE_NA) { dev_err(hdev->dev, "Queue index %d is invalid\n", chunk->queue_index); return -EINVAL; @@ -795,7 +811,7 @@ static int cs_ioctl_signal_wait(struct hl_fpriv *hpriv, enum hl_cs_type cs_type, hw_queue_prop = &hdev->asic_prop.hw_queues_props[q_idx]; q_type = hw_queue_prop->type; - if ((q_idx >= HL_MAX_QUEUES) || + if ((q_idx >= hdev->asic_prop.max_queues) || (!hw_queue_prop->supports_sync_stream)) { dev_err(hdev->dev, "Queue index %d is invalid\n", q_idx); rc = -EINVAL; diff --git a/drivers/misc/habanalabs/gaudi/gaudi.c b/drivers/misc/habanalabs/gaudi/gaudi.c index eede6c33a37f..7eee4a10154b 100644 --- a/drivers/misc/habanalabs/gaudi/gaudi.c +++ b/drivers/misc/habanalabs/gaudi/gaudi.c @@ -340,14 +340,15 @@ static int gaudi_get_fixed_properties(struct hl_device *hdev) struct asic_fixed_properties *prop = &hdev->asic_prop; int i; - if (GAUDI_QUEUE_ID_SIZE >= HL_MAX_QUEUES) { - dev_err(hdev->dev, - "Number of H/W queues must be smaller than %d\n", - HL_MAX_QUEUES); - return -EFAULT; - } + prop->max_queues = GAUDI_QUEUE_ID_SIZE; + prop->hw_queues_props = kcalloc(prop->max_queues, + sizeof(struct hw_queue_properties), + GFP_KERNEL); - for (i = 0 ; i < GAUDI_QUEUE_ID_SIZE ; i++) { + if (!prop->hw_queues_props) + return -ENOMEM; + + for (i = 0 ; i < prop->max_queues ; i++) { if (gaudi_queue_type[i] == QUEUE_TYPE_EXT) { prop->hw_queues_props[i].type = QUEUE_TYPE_EXT; prop->hw_queues_props[i].driver_only = 0; @@ -370,9 +371,6 @@ static int gaudi_get_fixed_properties(struct hl_device *hdev) } } - for (; i < HL_MAX_QUEUES; i++) - prop->hw_queues_props[i].type = QUEUE_TYPE_NA; - prop->completion_queues_count = NUMBER_OF_CMPLT_QUEUES; prop->sync_stream_first_sob = 0; prop->sync_stream_first_mon = 0; @@ -548,7 +546,8 @@ static int gaudi_early_init(struct hl_device *hdev) (unsigned long long) pci_resource_len(pdev, SRAM_BAR_ID), SRAM_BAR_SIZE); - return -ENODEV; + rc = -ENODEV; + goto free_queue_props; } if (pci_resource_len(pdev, CFG_BAR_ID) != CFG_BAR_SIZE) { @@ -558,20 +557,26 @@ static int gaudi_early_init(struct hl_device *hdev) (unsigned long long) pci_resource_len(pdev, CFG_BAR_ID), CFG_BAR_SIZE); - return -ENODEV; + rc = -ENODEV; + goto free_queue_props; } prop->dram_pci_bar_size = pci_resource_len(pdev, HBM_BAR_ID); rc = hl_pci_init(hdev); if (rc) - return rc; + goto free_queue_props; return 0; + +free_queue_props: + kfree(hdev->asic_prop.hw_queues_props); + return rc; } static int gaudi_early_fini(struct hl_device *hdev) { + kfree(hdev->asic_prop.hw_queues_props); hl_pci_fini(hdev); return 0; @@ -3461,7 +3466,7 @@ static int gaudi_test_queues(struct hl_device *hdev) { int i, rc, ret_val = 0; - for (i = 0 ; i < HL_MAX_QUEUES ; i++) { + for (i = 0 ; i < hdev->asic_prop.max_queues ; i++) { if (hdev->asic_prop.hw_queues_props[i].type == QUEUE_TYPE_EXT) { rc = gaudi_test_queue(hdev, i); if (rc) diff --git a/drivers/misc/habanalabs/goya/goya.c b/drivers/misc/habanalabs/goya/goya.c index 5839b5bc9ee3..36db771f391c 100644 --- a/drivers/misc/habanalabs/goya/goya.c +++ b/drivers/misc/habanalabs/goya/goya.c @@ -337,11 +337,19 @@ static int goya_mmu_set_dram_default_page(struct hl_device *hdev); static int goya_mmu_add_mappings_for_device_cpu(struct hl_device *hdev); static void goya_mmu_prepare(struct hl_device *hdev, u32 asid); -void goya_get_fixed_properties(struct hl_device *hdev) +int goya_get_fixed_properties(struct hl_device *hdev) { struct asic_fixed_properties *prop = &hdev->asic_prop; int i; + prop->max_queues = GOYA_QUEUE_ID_SIZE; + prop->hw_queues_props = kcalloc(prop->max_queues, + sizeof(struct hw_queue_properties), + GFP_KERNEL); + + if (!prop->hw_queues_props) + return -ENOMEM; + for (i = 0 ; i < NUMBER_OF_EXT_HW_QUEUES ; i++) { prop->hw_queues_props[i].type = QUEUE_TYPE_EXT; prop->hw_queues_props[i].driver_only = 0; @@ -361,9 +369,6 @@ void goya_get_fixed_properties(struct hl_device *hdev) prop->hw_queues_props[i].requires_kernel_cb = 0; } - for (; i < HL_MAX_QUEUES; i++) - prop->hw_queues_props[i].type = QUEUE_TYPE_NA; - prop->completion_queues_count = NUMBER_OF_CMPLT_QUEUES; prop->dram_base_address = DRAM_PHYS_BASE; @@ -428,6 +433,8 @@ void goya_get_fixed_properties(struct hl_device *hdev) CARD_NAME_MAX_LEN); prop->max_pending_cs = GOYA_MAX_PENDING_CS; + + return 0; } /* @@ -540,7 +547,11 @@ static int goya_early_init(struct hl_device *hdev) u32 val; int rc; - goya_get_fixed_properties(hdev); + rc = goya_get_fixed_properties(hdev); + if (rc) { + dev_err(hdev->dev, "Failed to get fixed properties\n"); + return rc; + } /* Check BAR sizes */ if (pci_resource_len(pdev, SRAM_CFG_BAR_ID) != CFG_BAR_SIZE) { @@ -550,7 +561,8 @@ static int goya_early_init(struct hl_device *hdev) (unsigned long long) pci_resource_len(pdev, SRAM_CFG_BAR_ID), CFG_BAR_SIZE); - return -ENODEV; + rc = -ENODEV; + goto free_queue_props; } if (pci_resource_len(pdev, MSIX_BAR_ID) != MSIX_BAR_SIZE) { @@ -560,14 +572,15 @@ static int goya_early_init(struct hl_device *hdev) (unsigned long long) pci_resource_len(pdev, MSIX_BAR_ID), MSIX_BAR_SIZE); - return -ENODEV; + rc = -ENODEV; + goto free_queue_props; } prop->dram_pci_bar_size = pci_resource_len(pdev, DDR_BAR_ID); rc = hl_pci_init(hdev); if (rc) - return rc; + goto free_queue_props; if (!hdev->pldm) { val = RREG32(mmPSOC_GLOBAL_CONF_BOOT_STRAP_PINS); @@ -577,6 +590,10 @@ static int goya_early_init(struct hl_device *hdev) } return 0; + +free_queue_props: + kfree(hdev->asic_prop.hw_queues_props); + return rc; } /* @@ -589,6 +606,7 @@ static int goya_early_init(struct hl_device *hdev) */ static int goya_early_fini(struct hl_device *hdev) { + kfree(hdev->asic_prop.hw_queues_props); hl_pci_fini(hdev); return 0; diff --git a/drivers/misc/habanalabs/goya/goyaP.h b/drivers/misc/habanalabs/goya/goyaP.h index 9d8a1761252d..8265cc21b45a 100644 --- a/drivers/misc/habanalabs/goya/goyaP.h +++ b/drivers/misc/habanalabs/goya/goyaP.h @@ -31,10 +31,6 @@ */ #define NUMBER_OF_INTERRUPTS (NUMBER_OF_CMPLT_QUEUES + 1) -#if (NUMBER_OF_HW_QUEUES >= HL_MAX_QUEUES) -#error "Number of H/W queues must be smaller than HL_MAX_QUEUES" -#endif - #if (NUMBER_OF_INTERRUPTS > GOYA_MSIX_ENTRIES) #error "Number of MSIX interrupts must be smaller or equal to GOYA_MSIX_ENTRIES" #endif @@ -170,7 +166,7 @@ struct goya_device { u8 device_cpu_mmu_mappings_done; }; -void goya_get_fixed_properties(struct hl_device *hdev); +int goya_get_fixed_properties(struct hl_device *hdev); int goya_mmu_init(struct hl_device *hdev); void goya_init_dma_qmans(struct hl_device *hdev); void goya_init_mme_qmans(struct hl_device *hdev); diff --git a/drivers/misc/habanalabs/habanalabs.h b/drivers/misc/habanalabs/habanalabs.h index 365236589bbf..9213d107b533 100644 --- a/drivers/misc/habanalabs/habanalabs.h +++ b/drivers/misc/habanalabs/habanalabs.h @@ -41,8 +41,6 @@ #define HL_SIM_MAX_TIMEOUT_US 10000000 /* 10s */ -#define HL_MAX_QUEUES 128 - #define HL_IDLE_BUSY_TS_ARR_SIZE 4096 /* Memory */ @@ -290,14 +288,15 @@ struct hl_mmu_properties { * @high_pll: high PLL frequency used by the device. * @cb_pool_cb_cnt: number of CBs in the CB pool. * @cb_pool_cb_size: size of each CB in the CB pool. - * @tpc_enabled_mask: which TPCs are enabled. + * @max_pending_cs: maximum of concurrent pending command submissions + * @max_queues: maximum amount of queues in the system * @sync_stream_first_sob: first sync object available for sync stream use * @sync_stream_first_mon: first monitor available for sync stream use * @tpc_enabled_mask: which TPCs are enabled. * @completion_queues_count: number of completion queues. */ struct asic_fixed_properties { - struct hw_queue_properties hw_queues_props[HL_MAX_QUEUES]; + struct hw_queue_properties *hw_queues_props; struct armcp_info armcp_info; char uboot_ver[VERSION_MAX_LEN]; char preboot_ver[VERSION_MAX_LEN]; @@ -336,6 +335,7 @@ struct asic_fixed_properties { u32 cb_pool_cb_cnt; u32 cb_pool_cb_size; u32 max_pending_cs; + u32 max_queues; u16 sync_stream_first_sob; u16 sync_stream_first_mon; u8 tpc_enabled_mask; @@ -901,7 +901,7 @@ struct hl_userptr { * @aborted: true if CS was aborted due to some device error. */ struct hl_cs { - u16 jobs_in_queue_cnt[HL_MAX_QUEUES]; + u16 *jobs_in_queue_cnt; struct hl_ctx *ctx; struct list_head job_list; spinlock_t job_lock; diff --git a/drivers/misc/habanalabs/hw_queue.c b/drivers/misc/habanalabs/hw_queue.c index da66ffb528f8..7965551587fc 100644 --- a/drivers/misc/habanalabs/hw_queue.c +++ b/drivers/misc/habanalabs/hw_queue.c @@ -46,7 +46,7 @@ void hl_int_hw_queue_update_ci(struct hl_cs *cs) goto out; q = &hdev->kernel_queues[0]; - for (i = 0 ; i < HL_MAX_QUEUES ; i++, q++) { + for (i = 0 ; i < hdev->asic_prop.max_queues ; i++, q++) { if (q->queue_type == QUEUE_TYPE_INT) { q->ci += cs->jobs_in_queue_cnt[i]; q->ci &= ((q->int_queue_len << 1) - 1); @@ -509,6 +509,7 @@ int hl_hw_queue_schedule_cs(struct hl_cs *cs) struct hl_device *hdev = ctx->hdev; struct hl_cs_job *job, *tmp; struct hl_hw_queue *q; + u32 max_queues; int rc = 0, i, cq_cnt; hdev->asic_funcs->hw_queues_lock(hdev); @@ -521,8 +522,10 @@ int hl_hw_queue_schedule_cs(struct hl_cs *cs) goto out; } + max_queues = hdev->asic_prop.max_queues; + q = &hdev->kernel_queues[0]; - for (i = 0, cq_cnt = 0 ; i < HL_MAX_QUEUES ; i++, q++) { + for (i = 0, cq_cnt = 0 ; i < max_queues ; i++, q++) { if (cs->jobs_in_queue_cnt[i]) { switch (q->queue_type) { case QUEUE_TYPE_EXT: @@ -601,7 +604,7 @@ int hl_hw_queue_schedule_cs(struct hl_cs *cs) unroll_cq_resv: q = &hdev->kernel_queues[0]; - for (i = 0 ; (i < HL_MAX_QUEUES) && (cq_cnt > 0) ; i++, q++) { + for (i = 0 ; (i < max_queues) && (cq_cnt > 0) ; i++, q++) { if ((q->queue_type == QUEUE_TYPE_EXT || q->queue_type == QUEUE_TYPE_HW) && cs->jobs_in_queue_cnt[i]) { @@ -872,7 +875,7 @@ int hl_hw_queues_create(struct hl_device *hdev) struct hl_hw_queue *q; int i, rc, q_ready_cnt; - hdev->kernel_queues = kcalloc(HL_MAX_QUEUES, + hdev->kernel_queues = kcalloc(asic->max_queues, sizeof(*hdev->kernel_queues), GFP_KERNEL); if (!hdev->kernel_queues) { @@ -882,7 +885,7 @@ int hl_hw_queues_create(struct hl_device *hdev) /* Initialize the H/W queues */ for (i = 0, q_ready_cnt = 0, q = hdev->kernel_queues; - i < HL_MAX_QUEUES ; i++, q_ready_cnt++, q++) { + i < asic->max_queues ; i++, q_ready_cnt++, q++) { q->queue_type = asic->hw_queues_props[i].type; q->supports_sync_stream = @@ -909,9 +912,10 @@ release_queues: void hl_hw_queues_destroy(struct hl_device *hdev) { struct hl_hw_queue *q; + u32 max_queues = hdev->asic_prop.max_queues; int i; - for (i = 0, q = hdev->kernel_queues ; i < HL_MAX_QUEUES ; i++, q++) + for (i = 0, q = hdev->kernel_queues ; i < max_queues ; i++, q++) queue_fini(hdev, q); kfree(hdev->kernel_queues); @@ -920,9 +924,10 @@ void hl_hw_queues_destroy(struct hl_device *hdev) void hl_hw_queue_reset(struct hl_device *hdev, bool hard_reset) { struct hl_hw_queue *q; + u32 max_queues = hdev->asic_prop.max_queues; int i; - for (i = 0, q = hdev->kernel_queues ; i < HL_MAX_QUEUES ; i++, q++) { + for (i = 0, q = hdev->kernel_queues ; i < max_queues ; i++, q++) { if ((!q->valid) || ((!hard_reset) && (q->queue_type == QUEUE_TYPE_CPU))) continue; -- cgit v1.2.3-59-g8ed1b From 79b1894c419468607ce547855e8636d42b456149 Mon Sep 17 00:00:00 2001 From: Ofir Bitton Date: Wed, 24 Jun 2020 14:49:43 +0300 Subject: habanalabs: use queue pi/ci in order to determine queue occupancy Instead of using the free slots amount on the compute CQ to determine whether we can submit work to queues, use the queues pi/ci. This is needed in future ASICs where we don't have CQ per queue. Signed-off-by: Ofir Bitton Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/device.c | 17 +++++--- drivers/misc/habanalabs/habanalabs.h | 2 +- drivers/misc/habanalabs/hw_queue.c | 82 ++++++++++++------------------------ drivers/misc/habanalabs/irq.c | 7 +-- 4 files changed, 39 insertions(+), 69 deletions(-) diff --git a/drivers/misc/habanalabs/device.c b/drivers/misc/habanalabs/device.c index 2b38a119704c..65a5a5c52a48 100644 --- a/drivers/misc/habanalabs/device.c +++ b/drivers/misc/habanalabs/device.c @@ -1144,14 +1144,17 @@ int hl_device_init(struct hl_device *hdev, struct class *hclass) * because there the addresses of the completion queues are being * passed as arguments to request_irq */ - hdev->completion_queue = kcalloc(cq_cnt, - sizeof(*hdev->completion_queue), - GFP_KERNEL); + if (cq_cnt) { + hdev->completion_queue = kcalloc(cq_cnt, + sizeof(*hdev->completion_queue), + GFP_KERNEL); - if (!hdev->completion_queue) { - dev_err(hdev->dev, "failed to allocate completion queues\n"); - rc = -ENOMEM; - goto hw_queues_destroy; + if (!hdev->completion_queue) { + dev_err(hdev->dev, + "failed to allocate completion queues\n"); + rc = -ENOMEM; + goto hw_queues_destroy; + } } for (i = 0, cq_ready_cnt = 0 ; i < cq_cnt ; i++, cq_ready_cnt++) { diff --git a/drivers/misc/habanalabs/habanalabs.h b/drivers/misc/habanalabs/habanalabs.h index 9213d107b533..a61aab09778c 100644 --- a/drivers/misc/habanalabs/habanalabs.h +++ b/drivers/misc/habanalabs/habanalabs.h @@ -461,7 +461,7 @@ struct hl_hw_queue { u64 kernel_address; dma_addr_t bus_address; u32 pi; - u32 ci; + atomic_t ci; u32 hw_queue_id; u32 cq_id; u32 msi_vec; diff --git a/drivers/misc/habanalabs/hw_queue.c b/drivers/misc/habanalabs/hw_queue.c index 7965551587fc..474a0e8a7797 100644 --- a/drivers/misc/habanalabs/hw_queue.c +++ b/drivers/misc/habanalabs/hw_queue.c @@ -23,10 +23,14 @@ inline u32 hl_hw_queue_add_ptr(u32 ptr, u16 val) ptr &= ((HL_QUEUE_LENGTH << 1) - 1); return ptr; } +static inline int queue_ci_get(atomic_t *ci, u32 queue_len) +{ + return atomic_read(ci) & ((queue_len << 1) - 1); +} static inline int queue_free_slots(struct hl_hw_queue *q, u32 queue_len) { - int delta = (q->pi - q->ci); + int delta = (q->pi - queue_ci_get(&q->ci, queue_len)); if (delta >= 0) return (queue_len - delta); @@ -40,21 +44,14 @@ void hl_int_hw_queue_update_ci(struct hl_cs *cs) struct hl_hw_queue *q; int i; - hdev->asic_funcs->hw_queues_lock(hdev); - if (hdev->disabled) - goto out; + return; q = &hdev->kernel_queues[0]; for (i = 0 ; i < hdev->asic_prop.max_queues ; i++, q++) { - if (q->queue_type == QUEUE_TYPE_INT) { - q->ci += cs->jobs_in_queue_cnt[i]; - q->ci &= ((q->int_queue_len << 1) - 1); - } + if (q->queue_type == QUEUE_TYPE_INT) + atomic_add(cs->jobs_in_queue_cnt[i], &q->ci); } - -out: - hdev->asic_funcs->hw_queues_unlock(hdev); } /* @@ -174,38 +171,26 @@ static int int_queue_sanity_checks(struct hl_device *hdev, } /* - * hw_queue_sanity_checks() - Perform some sanity checks on a H/W queue. + * hw_queue_sanity_checks() - Make sure we have enough space in the h/w queue * @hdev: Pointer to hl_device structure. * @q: Pointer to hl_hw_queue structure. * @num_of_entries: How many entries to check for space. * - * Perform the following: - * - Make sure we have enough space in the completion queue. - * This check also ensures that there is enough space in the h/w queue, as - * both queues are of the same size. - * - Reserve space in the completion queue (needs to be reversed if there - * is a failure down the road before the actual submission of work). + * Notice: We do not reserve queue entries so this function mustn't be called + * more than once per CS for the same queue * - * Both operations are done using the "free_slots_cnt" field of the completion - * queue. The CI counters of the queue and the completion queue are not - * needed/used for the H/W queue type. */ static int hw_queue_sanity_checks(struct hl_device *hdev, struct hl_hw_queue *q, int num_of_entries) { - atomic_t *free_slots = - &hdev->completion_queue[q->cq_id].free_slots_cnt; + int free_slots_cnt; - /* - * Check we have enough space in the completion queue. - * Add -1 to counter (decrement) unless counter was already 0. - * In that case, CQ is full so we can't submit a new CB. - * atomic_add_unless will return 0 if counter was already 0. - */ - if (atomic_add_negative(num_of_entries * -1, free_slots)) { - dev_dbg(hdev->dev, "No space for %d entries on CQ %d\n", - num_of_entries, q->hw_queue_id); - atomic_add(num_of_entries, free_slots); + /* Check we have enough space in the queue */ + free_slots_cnt = queue_free_slots(q, HL_QUEUE_LENGTH); + + if (free_slots_cnt < num_of_entries) { + dev_dbg(hdev->dev, "Queue %d doesn't have room for %d CBs\n", + q->hw_queue_id, num_of_entries); return -EAGAIN; } @@ -366,7 +351,6 @@ static void hw_queue_schedule_job(struct hl_cs_job *job) { struct hl_device *hdev = job->cs->ctx->hdev; struct hl_hw_queue *q = &hdev->kernel_queues[job->hw_queue_id]; - struct hl_cq *cq; u64 ptr; u32 offset, ctl, len; @@ -395,17 +379,6 @@ static void hw_queue_schedule_job(struct hl_cs_job *job) else ptr = (u64) (uintptr_t) job->user_cb; - /* - * No need to protect pi_offset because scheduling to the - * H/W queues is done under the scheduler mutex - * - * No need to check if CQ is full because it was already - * checked in hw_queue_sanity_checks - */ - cq = &hdev->completion_queue[q->cq_id]; - - cq->pi = hl_cq_inc_ptr(cq->pi); - ext_and_hw_queue_submit_bd(hdev, q, ctl, len, ptr); } @@ -552,8 +525,7 @@ int hl_hw_queue_schedule_cs(struct hl_cs *cs) goto unroll_cq_resv; } - if (q->queue_type == QUEUE_TYPE_EXT || - q->queue_type == QUEUE_TYPE_HW) + if (q->queue_type == QUEUE_TYPE_EXT) cq_cnt++; } } @@ -605,9 +577,8 @@ int hl_hw_queue_schedule_cs(struct hl_cs *cs) unroll_cq_resv: q = &hdev->kernel_queues[0]; for (i = 0 ; (i < max_queues) && (cq_cnt > 0) ; i++, q++) { - if ((q->queue_type == QUEUE_TYPE_EXT || - q->queue_type == QUEUE_TYPE_HW) && - cs->jobs_in_queue_cnt[i]) { + if ((q->queue_type == QUEUE_TYPE_EXT) && + (cs->jobs_in_queue_cnt[i])) { atomic_t *free_slots = &hdev->completion_queue[i].free_slots_cnt; atomic_add(cs->jobs_in_queue_cnt[i], free_slots); @@ -631,7 +602,7 @@ void hl_hw_queue_inc_ci_kernel(struct hl_device *hdev, u32 hw_queue_id) { struct hl_hw_queue *q = &hdev->kernel_queues[hw_queue_id]; - q->ci = hl_queue_inc_ptr(q->ci); + atomic_inc(&q->ci); } static int ext_and_cpu_queue_init(struct hl_device *hdev, struct hl_hw_queue *q, @@ -666,7 +637,7 @@ static int ext_and_cpu_queue_init(struct hl_device *hdev, struct hl_hw_queue *q, } /* Make sure read/write pointers are initialized to start of queue */ - q->ci = 0; + atomic_set(&q->ci, 0); q->pi = 0; return 0; @@ -700,7 +671,7 @@ static int int_queue_init(struct hl_device *hdev, struct hl_hw_queue *q) q->kernel_address = (u64) (uintptr_t) p; q->pi = 0; - q->ci = 0; + atomic_set(&q->ci, 0); return 0; } @@ -729,7 +700,7 @@ static int hw_queue_init(struct hl_device *hdev, struct hl_hw_queue *q) q->kernel_address = (u64) (uintptr_t) p; /* Make sure read/write pointers are initialized to start of queue */ - q->ci = 0; + atomic_set(&q->ci, 0); q->pi = 0; return 0; @@ -931,7 +902,8 @@ void hl_hw_queue_reset(struct hl_device *hdev, bool hard_reset) if ((!q->valid) || ((!hard_reset) && (q->queue_type == QUEUE_TYPE_CPU))) continue; - q->pi = q->ci = 0; + q->pi = 0; + atomic_set(&q->ci, 0); if (q->supports_sync_stream) sync_stream_queue_reset(hdev, q->hw_queue_id); diff --git a/drivers/misc/habanalabs/irq.c b/drivers/misc/habanalabs/irq.c index 7a4878edb1a3..195a5ecba0e8 100644 --- a/drivers/misc/habanalabs/irq.c +++ b/drivers/misc/habanalabs/irq.c @@ -122,12 +122,7 @@ irqreturn_t hl_irq_handler_cq(int irq, void *arg) queue_work(hdev->cq_wq, &job->finish_work); } - /* Update ci of the context's queue. There is no - * need to protect it with spinlock because this update is - * done only inside IRQ and there is a different IRQ per - * queue - */ - queue->ci = hl_queue_inc_ptr(queue->ci); + atomic_inc(&queue->ci); /* Clear CQ entry ready bit */ cq_entry->data = cpu_to_le32(le32_to_cpu(cq_entry->data) & -- cgit v1.2.3-59-g8ed1b From 9158c47e2059967038b19d051a1afd87954fbba4 Mon Sep 17 00:00:00 2001 From: Omer Shpigelman Date: Wed, 8 Jul 2020 00:29:54 +0300 Subject: habanalabs: remove unused hash Remove an old hash that is not in use anymore. Signed-off-by: Omer Shpigelman Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/habanalabs.h | 2 -- drivers/misc/habanalabs/mmu.c | 1 - 2 files changed, 3 deletions(-) diff --git a/drivers/misc/habanalabs/habanalabs.h b/drivers/misc/habanalabs/habanalabs.h index a61aab09778c..ea0fd178accb 100644 --- a/drivers/misc/habanalabs/habanalabs.h +++ b/drivers/misc/habanalabs/habanalabs.h @@ -780,7 +780,6 @@ struct hl_va_range { * struct hl_ctx - user/kernel context. * @mem_hash: holds mapping from virtual address to virtual memory area * descriptor (hl_vm_phys_pg_list or hl_userptr). - * @mmu_phys_hash: holds a mapping from physical address to pgt_info structure. * @mmu_shadow_hash: holds a mapping from shadow address to pgt_info structure. * @hpriv: pointer to the private (Kernel Driver) data of the process (fd). * @hdev: pointer to the device structure. @@ -814,7 +813,6 @@ struct hl_va_range { */ struct hl_ctx { DECLARE_HASHTABLE(mem_hash, MEM_HASH_TABLE_BITS); - DECLARE_HASHTABLE(mmu_phys_hash, MMU_HASH_TABLE_BITS); DECLARE_HASHTABLE(mmu_shadow_hash, MMU_HASH_TABLE_BITS); struct hl_fpriv *hpriv; struct hl_device *hdev; diff --git a/drivers/misc/habanalabs/mmu.c b/drivers/misc/habanalabs/mmu.c index a290d6b49d78..04303950e630 100644 --- a/drivers/misc/habanalabs/mmu.c +++ b/drivers/misc/habanalabs/mmu.c @@ -502,7 +502,6 @@ int hl_mmu_ctx_init(struct hl_ctx *ctx) return 0; mutex_init(&ctx->mmu_lock); - hash_init(ctx->mmu_phys_hash); hash_init(ctx->mmu_shadow_hash); return dram_default_mapping_init(ctx); -- cgit v1.2.3-59-g8ed1b From c83c4171933bc4ebd147efb6bbdb787b25d1907d Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Sun, 5 Jul 2020 15:48:34 +0300 Subject: habanalabs: halt device CPU only upon certain reset Currently the driver halts the device CPU in the halt engines function, which halts all the engines of the ASIC. The problem is that if later on we stop the reset process (due to inability to clean memory mappings in time), the CPU will remain in halt mode. This creates many issues, such as thermal/power control and FLR handling. Therefore, move the halting of the device CPU to the very end of the reset process, just before writing to the registers to initiate the reset. In addition, the driver now needs to send a message to the device F/W to disable it from sending interrupts to the host machine because during halt engines function the driver disables the MSI/MSI-X interrupts. Signed-off-by: Oded Gabbay Reviewed-by: Tomer Tayar --- drivers/misc/habanalabs/device.c | 16 +++++++++ drivers/misc/habanalabs/gaudi/gaudi.c | 40 +++++++++++++--------- drivers/misc/habanalabs/goya/goya.c | 38 ++++++++++---------- .../habanalabs/include/gaudi/asic_reg/gaudi_regs.h | 1 + .../misc/habanalabs/include/gaudi/gaudi_masks.h | 3 ++ 5 files changed, 61 insertions(+), 37 deletions(-) diff --git a/drivers/misc/habanalabs/device.c b/drivers/misc/habanalabs/device.c index 65a5a5c52a48..df709767c7ea 100644 --- a/drivers/misc/habanalabs/device.c +++ b/drivers/misc/habanalabs/device.c @@ -838,6 +838,22 @@ int hl_device_reset(struct hl_device *hdev, bool hard_reset, if (rc) return 0; + if (hard_reset) { + /* Disable PCI access from device F/W so he won't send + * us additional interrupts. We disable MSI/MSI-X at + * the halt_engines function and we can't have the F/W + * sending us interrupts after that. We need to disable + * the access here because if the device is marked + * disable, the message won't be send. Also, in case + * of heartbeat, the device CPU is marked as disable + * so this message won't be sent + */ + if (hl_fw_send_pci_access_msg(hdev, + ARMCP_PACKET_DISABLE_PCI_ACCESS)) + dev_warn(hdev->dev, + "Failed to disable PCI access by F/W\n"); + } + /* This also blocks future CS/VM/JOB completion operations */ hdev->disabled = true; diff --git a/drivers/misc/habanalabs/gaudi/gaudi.c b/drivers/misc/habanalabs/gaudi/gaudi.c index 7eee4a10154b..a9fd3d352ef0 100644 --- a/drivers/misc/habanalabs/gaudi/gaudi.c +++ b/drivers/misc/habanalabs/gaudi/gaudi.c @@ -2578,27 +2578,16 @@ static void gaudi_disable_timestamp(struct hl_device *hdev) static void gaudi_halt_engines(struct hl_device *hdev, bool hard_reset) { - u32 wait_timeout_ms, cpu_timeout_ms; + u32 wait_timeout_ms; dev_info(hdev->dev, "Halting compute engines and disabling interrupts\n"); - if (hdev->pldm) { + if (hdev->pldm) wait_timeout_ms = GAUDI_PLDM_RESET_WAIT_MSEC; - cpu_timeout_ms = GAUDI_PLDM_RESET_WAIT_MSEC; - } else { + else wait_timeout_ms = GAUDI_RESET_WAIT_MSEC; - cpu_timeout_ms = GAUDI_CPU_RESET_WAIT_MSEC; - } - /* - * I don't know what is the state of the CPU so make sure it is - * stopped in any means necessary - */ - WREG32(mmPSOC_GLOBAL_CONF_KMD_MSG_TO_CPU, KMD_MSG_GOTO_WFE); - WREG32(mmGIC_DISTRIBUTOR__5_GICD_SETSPI_NSR, - GAUDI_EVENT_HALT_MACHINE); - msleep(cpu_timeout_ms); gaudi_stop_mme_qmans(hdev); gaudi_stop_tpc_qmans(hdev); @@ -2966,17 +2955,34 @@ disable_queues: static void gaudi_hw_fini(struct hl_device *hdev, bool hard_reset) { struct gaudi_device *gaudi = hdev->asic_specific; - u32 status, reset_timeout_ms, boot_strap = 0; + u32 status, reset_timeout_ms, cpu_timeout_ms, boot_strap = 0; if (!hard_reset) { dev_err(hdev->dev, "GAUDI doesn't support soft-reset\n"); return; } - if (hdev->pldm) + if (hdev->pldm) { reset_timeout_ms = GAUDI_PLDM_HRESET_TIMEOUT_MSEC; - else + cpu_timeout_ms = GAUDI_PLDM_RESET_WAIT_MSEC; + } else { reset_timeout_ms = GAUDI_RESET_TIMEOUT_MSEC; + cpu_timeout_ms = GAUDI_CPU_RESET_WAIT_MSEC; + } + + /* Set device to handle FLR by H/W as we will put the device CPU to + * halt mode + */ + WREG32(mmPCIE_AUX_FLR_CTRL, (PCIE_AUX_FLR_CTRL_HW_CTRL_MASK | + PCIE_AUX_FLR_CTRL_INT_MASK_MASK)); + + /* I don't know what is the state of the CPU so make sure it is + * stopped in any means necessary + */ + WREG32(mmPSOC_GLOBAL_CONF_KMD_MSG_TO_CPU, KMD_MSG_GOTO_WFE); + WREG32(mmGIC_DISTRIBUTOR__5_GICD_SETSPI_NSR, GAUDI_EVENT_HALT_MACHINE); + + msleep(cpu_timeout_ms); /* Tell ASIC not to re-initialize PCIe */ WREG32(mmPREBOOT_PCIE_EN, LKD_HARD_RESET_MAGIC); diff --git a/drivers/misc/habanalabs/goya/goya.c b/drivers/misc/habanalabs/goya/goya.c index 36db771f391c..2b0937d950c1 100644 --- a/drivers/misc/habanalabs/goya/goya.c +++ b/drivers/misc/habanalabs/goya/goya.c @@ -2240,29 +2240,15 @@ static void goya_disable_timestamp(struct hl_device *hdev) static void goya_halt_engines(struct hl_device *hdev, bool hard_reset) { - u32 wait_timeout_ms, cpu_timeout_ms; + u32 wait_timeout_ms; dev_info(hdev->dev, "Halting compute engines and disabling interrupts\n"); - if (hdev->pldm) { + if (hdev->pldm) wait_timeout_ms = GOYA_PLDM_RESET_WAIT_MSEC; - cpu_timeout_ms = GOYA_PLDM_RESET_WAIT_MSEC; - } else { + else wait_timeout_ms = GOYA_RESET_WAIT_MSEC; - cpu_timeout_ms = GOYA_CPU_RESET_WAIT_MSEC; - } - - if (hard_reset) { - /* - * I don't know what is the state of the CPU so make sure it is - * stopped in any means necessary - */ - WREG32(mmPSOC_GLOBAL_CONF_UBOOT_MAGIC, KMD_MSG_GOTO_WFE); - WREG32(mmGIC_DISTRIBUTOR__5_GICD_SETSPI_NSR, - GOYA_ASYNC_EVENT_ID_HALT_MACHINE); - msleep(cpu_timeout_ms); - } goya_stop_external_queues(hdev); goya_stop_internal_queues(hdev); @@ -2567,14 +2553,26 @@ disable_queues: static void goya_hw_fini(struct hl_device *hdev, bool hard_reset) { struct goya_device *goya = hdev->asic_specific; - u32 reset_timeout_ms, status; + u32 reset_timeout_ms, cpu_timeout_ms, status; - if (hdev->pldm) + if (hdev->pldm) { reset_timeout_ms = GOYA_PLDM_RESET_TIMEOUT_MSEC; - else + cpu_timeout_ms = GOYA_PLDM_RESET_WAIT_MSEC; + } else { reset_timeout_ms = GOYA_RESET_TIMEOUT_MSEC; + cpu_timeout_ms = GOYA_CPU_RESET_WAIT_MSEC; + } if (hard_reset) { + /* I don't know what is the state of the CPU so make sure it is + * stopped in any means necessary + */ + WREG32(mmPSOC_GLOBAL_CONF_UBOOT_MAGIC, KMD_MSG_GOTO_WFE); + WREG32(mmGIC_DISTRIBUTOR__5_GICD_SETSPI_NSR, + GOYA_ASYNC_EVENT_ID_HALT_MACHINE); + + msleep(cpu_timeout_ms); + goya_set_ddr_bar_base(hdev, DRAM_PHYS_BASE); goya_disable_clk_rlx(hdev); goya_set_pll_refclk(hdev); diff --git a/drivers/misc/habanalabs/include/gaudi/asic_reg/gaudi_regs.h b/drivers/misc/habanalabs/include/gaudi/asic_reg/gaudi_regs.h index 0c75d43532bd..f92dc53af074 100644 --- a/drivers/misc/habanalabs/include/gaudi/asic_reg/gaudi_regs.h +++ b/drivers/misc/habanalabs/include/gaudi/asic_reg/gaudi_regs.h @@ -292,6 +292,7 @@ #define mmPCIE_DBI_DEVICE_ID_VENDOR_ID_REG 0xC02000 +#define mmPCIE_AUX_FLR_CTRL 0xC07394 #define mmPCIE_AUX_DBI 0xC07490 #endif /* ASIC_REG_GAUDI_REGS_H_ */ diff --git a/drivers/misc/habanalabs/include/gaudi/gaudi_masks.h b/drivers/misc/habanalabs/include/gaudi/gaudi_masks.h index 96f08050ef0f..13ef6b2887fd 100644 --- a/drivers/misc/habanalabs/include/gaudi/gaudi_masks.h +++ b/drivers/misc/habanalabs/include/gaudi/gaudi_masks.h @@ -455,4 +455,7 @@ enum axi_id { QM_ARB_ERR_MSG_EN_CHOISE_WDT_MASK |\ QM_ARB_ERR_MSG_EN_AXI_LBW_ERR_MASK) +#define PCIE_AUX_FLR_CTRL_HW_CTRL_MASK 0x1 +#define PCIE_AUX_FLR_CTRL_INT_MASK_MASK 0x2 + #endif /* GAUDI_MASKS_H_ */ -- cgit v1.2.3-59-g8ed1b From 5574cb2194b13de78df68cd32655ddbe619b1251 Mon Sep 17 00:00:00 2001 From: Ofir Bitton Date: Sun, 5 Jul 2020 13:35:51 +0300 Subject: habanalabs: Assign each CQ with its own work queue We identified a possible race during job completion when working with a single multi-threaded work queue. In order to overcome this race we suggest using a single threaded work queue per completion queue, hence we guarantee jobs completion in order. Signed-off-by: Ofir Bitton Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/command_submission.c | 4 ++- drivers/misc/habanalabs/device.c | 39 ++++++++++++++++++++++------ drivers/misc/habanalabs/habanalabs.h | 7 +++-- drivers/misc/habanalabs/irq.c | 2 +- 4 files changed, 40 insertions(+), 12 deletions(-) diff --git a/drivers/misc/habanalabs/command_submission.c b/drivers/misc/habanalabs/command_submission.c index 7769a1aacca1..c605be89f764 100644 --- a/drivers/misc/habanalabs/command_submission.c +++ b/drivers/misc/habanalabs/command_submission.c @@ -487,10 +487,12 @@ static void cs_rollback(struct hl_device *hdev, struct hl_cs *cs) void hl_cs_rollback_all(struct hl_device *hdev) { + int i; struct hl_cs *cs, *tmp; /* flush all completions */ - flush_workqueue(hdev->cq_wq); + for (i = 0 ; i < hdev->asic_prop.completion_queues_count ; i++) + flush_workqueue(hdev->cq_wq[i]); /* Make sure we don't have leftovers in the H/W queues mirror list */ list_for_each_entry_safe(cs, tmp, &hdev->hw_queues_mirror_list, diff --git a/drivers/misc/habanalabs/device.c b/drivers/misc/habanalabs/device.c index df709767c7ea..84800efec10d 100644 --- a/drivers/misc/habanalabs/device.c +++ b/drivers/misc/habanalabs/device.c @@ -249,7 +249,8 @@ static void device_cdev_sysfs_del(struct hl_device *hdev) */ static int device_early_init(struct hl_device *hdev) { - int rc; + int i, rc; + char workq_name[32]; switch (hdev->asic_type) { case ASIC_GOYA: @@ -274,11 +275,24 @@ static int device_early_init(struct hl_device *hdev) if (rc) goto early_fini; - hdev->cq_wq = alloc_workqueue("hl-free-jobs", WQ_UNBOUND, 0); - if (hdev->cq_wq == NULL) { - dev_err(hdev->dev, "Failed to allocate CQ workqueue\n"); - rc = -ENOMEM; - goto asid_fini; + if (hdev->asic_prop.completion_queues_count) { + hdev->cq_wq = kcalloc(hdev->asic_prop.completion_queues_count, + sizeof(*hdev->cq_wq), + GFP_ATOMIC); + if (!hdev->cq_wq) { + rc = -ENOMEM; + goto asid_fini; + } + } + + for (i = 0 ; i < hdev->asic_prop.completion_queues_count ; i++) { + snprintf(workq_name, 32, "hl-free-jobs-%u", i); + hdev->cq_wq[i] = create_singlethread_workqueue(workq_name); + if (hdev->cq_wq == NULL) { + dev_err(hdev->dev, "Failed to allocate CQ workqueue\n"); + rc = -ENOMEM; + goto free_cq_wq; + } } hdev->eq_wq = alloc_workqueue("hl-events", WQ_UNBOUND, 0); @@ -321,7 +335,10 @@ free_chip_info: free_eq_wq: destroy_workqueue(hdev->eq_wq); free_cq_wq: - destroy_workqueue(hdev->cq_wq); + for (i = 0 ; i < hdev->asic_prop.completion_queues_count ; i++) + if (hdev->cq_wq[i]) + destroy_workqueue(hdev->cq_wq[i]); + kfree(hdev->cq_wq); asid_fini: hl_asid_fini(hdev); early_fini: @@ -339,6 +356,8 @@ early_fini: */ static void device_early_fini(struct hl_device *hdev) { + int i; + mutex_destroy(&hdev->mmu_cache_lock); mutex_destroy(&hdev->debug_lock); mutex_destroy(&hdev->send_cpu_message_lock); @@ -351,7 +370,10 @@ static void device_early_fini(struct hl_device *hdev) kfree(hdev->hl_chip_info); destroy_workqueue(hdev->eq_wq); - destroy_workqueue(hdev->cq_wq); + + for (i = 0 ; i < hdev->asic_prop.completion_queues_count ; i++) + destroy_workqueue(hdev->cq_wq[i]); + kfree(hdev->cq_wq); hl_asid_fini(hdev); @@ -1181,6 +1203,7 @@ int hl_device_init(struct hl_device *hdev, struct class *hclass) "failed to initialize completion queue\n"); goto cq_fini; } + hdev->completion_queue[i].cq_idx = i; } /* diff --git a/drivers/misc/habanalabs/habanalabs.h b/drivers/misc/habanalabs/habanalabs.h index ea0fd178accb..01fb45887a5a 100644 --- a/drivers/misc/habanalabs/habanalabs.h +++ b/drivers/misc/habanalabs/habanalabs.h @@ -479,6 +479,7 @@ struct hl_hw_queue { * @hdev: pointer to the device structure * @kernel_address: holds the queue's kernel virtual address * @bus_address: holds the queue's DMA address + * @cq_idx: completion queue index in array * @hw_queue_id: the id of the matching H/W queue * @ci: ci inside the queue * @pi: pi inside the queue @@ -488,6 +489,7 @@ struct hl_cq { struct hl_device *hdev; u64 kernel_address; dma_addr_t bus_address; + u32 cq_idx; u32 hw_queue_id; u32 ci; u32 pi; @@ -1396,7 +1398,8 @@ struct hl_device_idle_busy_ts { * @asic_name: ASIC specific nmae. * @asic_type: ASIC specific type. * @completion_queue: array of hl_cq. - * @cq_wq: work queue of completion queues for executing work in process context + * @cq_wq: work queues of completion queues for executing work in process + * context. * @eq_wq: work queue of event queue for executing work in process context. * @kernel_ctx: Kernel driver context structure. * @kernel_queues: array of hl_hw_queue. @@ -1492,7 +1495,7 @@ struct hl_device { char asic_name[16]; enum hl_asic_type asic_type; struct hl_cq *completion_queue; - struct workqueue_struct *cq_wq; + struct workqueue_struct **cq_wq; struct workqueue_struct *eq_wq; struct hl_ctx *kernel_ctx; struct hl_hw_queue *kernel_queues; diff --git a/drivers/misc/habanalabs/irq.c b/drivers/misc/habanalabs/irq.c index 195a5ecba0e8..c8db717023f5 100644 --- a/drivers/misc/habanalabs/irq.c +++ b/drivers/misc/habanalabs/irq.c @@ -119,7 +119,7 @@ irqreturn_t hl_irq_handler_cq(int irq, void *arg) if ((shadow_index_valid) && (!hdev->disabled)) { job = queue->shadow_queue[hl_pi_2_offset(shadow_index)]; - queue_work(hdev->cq_wq, &job->finish_work); + queue_work(hdev->cq_wq[cq->cq_idx], &job->finish_work); } atomic_inc(&queue->ci); -- cgit v1.2.3-59-g8ed1b From 22cb855598ed9aa7812941eb29218dc6033b4b43 Mon Sep 17 00:00:00 2001 From: Ofir Bitton Date: Wed, 8 Jul 2020 10:16:27 +0300 Subject: habanalabs: verify queue can contain all cs jobs In order for the user to be aware of wrong inputs, we must return error in case the amount of jobs per cs exceeds the corresponding queue size. Signed-off-by: Ofir Bitton Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/habanalabs.h | 4 ++++ drivers/misc/habanalabs/hw_queue.c | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/drivers/misc/habanalabs/habanalabs.h b/drivers/misc/habanalabs/habanalabs.h index 01fb45887a5a..14def0d26d2d 100644 --- a/drivers/misc/habanalabs/habanalabs.h +++ b/drivers/misc/habanalabs/habanalabs.h @@ -421,6 +421,10 @@ struct hl_cs_job; #define HL_QUEUE_LENGTH 4096 #define HL_QUEUE_SIZE_IN_BYTES (HL_QUEUE_LENGTH * HL_BD_SIZE) +#if (HL_MAX_JOBS_PER_CS > HL_QUEUE_LENGTH) +#error "HL_QUEUE_LENGTH must be greater than HL_MAX_JOBS_PER_CS" +#endif + /* HL_CQ_LENGTH is in units of struct hl_cq_entry */ #define HL_CQ_LENGTH HL_QUEUE_LENGTH #define HL_CQ_SIZE_IN_BYTES (HL_CQ_LENGTH * HL_CQ_ENTRY_SIZE) diff --git a/drivers/misc/habanalabs/hw_queue.c b/drivers/misc/habanalabs/hw_queue.c index 474a0e8a7797..287681646071 100644 --- a/drivers/misc/habanalabs/hw_queue.c +++ b/drivers/misc/habanalabs/hw_queue.c @@ -158,6 +158,13 @@ static int int_queue_sanity_checks(struct hl_device *hdev, { int free_slots_cnt; + if (num_of_entries > q->int_queue_len) { + dev_err(hdev->dev, + "Cannot populate queue %u with %u jobs\n", + q->hw_queue_id, num_of_entries); + return -ENOMEM; + } + /* Check we have enough space in the queue */ free_slots_cnt = queue_free_slots(q, q->int_queue_len); -- cgit v1.2.3-59-g8ed1b From a9855a2d91531001f1a952a042f17cc42ef30cb7 Mon Sep 17 00:00:00 2001 From: Moti Haimovski Date: Wed, 24 Jun 2020 19:40:57 +0300 Subject: habanalabs: check for DMA errors when clearing memory In GAUDI we use QMAN0 DMA for clearing the MMU memory region at initialization. if this operation fails it places the DMA in an error state and then when trying to initialize QMAN0 we fail and erroneously assume its the QMAN that failed. This commit adds a check and clear of such DMA errors at initialization so we will have a better understanding of what went wrong. Signed-off-by: Moti Haimovski Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/gaudi/gaudi.c | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/drivers/misc/habanalabs/gaudi/gaudi.c b/drivers/misc/habanalabs/gaudi/gaudi.c index a9fd3d352ef0..57b2b9392cb2 100644 --- a/drivers/misc/habanalabs/gaudi/gaudi.c +++ b/drivers/misc/habanalabs/gaudi/gaudi.c @@ -4253,7 +4253,7 @@ static int gaudi_memset_device_memory(struct hl_device *hdev, u64 addr, { struct packet_lin_dma *lin_dma_pkt; struct hl_cs_job *job; - u32 cb_size, ctl; + u32 cb_size, ctl, err_cause; struct hl_cb *cb; int rc; @@ -4282,6 +4282,15 @@ static int gaudi_memset_device_memory(struct hl_device *hdev, u64 addr, goto release_cb; } + /* Verify DMA is OK */ + err_cause = RREG32(mmDMA0_CORE_ERR_CAUSE); + if (err_cause && !hdev->init_done) { + dev_dbg(hdev->dev, + "Clearing DMA0 engine from errors (cause 0x%x)\n", + err_cause); + WREG32(mmDMA0_CORE_ERR_CAUSE, err_cause); + } + job->id = 0; job->user_cb = cb; job->user_cb->cs_cnt++; @@ -4293,11 +4302,23 @@ static int gaudi_memset_device_memory(struct hl_device *hdev, u64 addr, hl_debugfs_add_job(hdev, job); rc = gaudi_send_job_on_qman0(hdev, job); - hl_debugfs_remove_job(hdev, job); kfree(job); cb->cs_cnt--; + /* Verify DMA is OK */ + err_cause = RREG32(mmDMA0_CORE_ERR_CAUSE); + if (err_cause) { + dev_err(hdev->dev, "DMA Failed, cause 0x%x\n", err_cause); + rc = -EIO; + if (!hdev->init_done) { + dev_dbg(hdev->dev, + "Clearing DMA0 engine from errors (cause 0x%x)\n", + err_cause); + WREG32(mmDMA0_CORE_ERR_CAUSE, err_cause); + } + } + release_cb: hl_cb_put(cb); hl_cb_destroy(hdev, &hdev->kernel_cb_mgr, cb->id << PAGE_SHIFT); -- cgit v1.2.3-59-g8ed1b From 70b2f993ea4a79c298aac4ec1c58089020536ba5 Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Mon, 13 Jul 2020 12:21:04 +0300 Subject: habanalabs: create common folder For internal needs of our CI we need to move all the common code into a common folder instead of putting them in the root folder of the driver. Same applies to the common header files under include/ Signed-off-by: Oded Gabbay Reviewed-by: Omer Shpigelman --- drivers/misc/habanalabs/Makefile | 11 +- drivers/misc/habanalabs/asid.c | 57 - drivers/misc/habanalabs/command_buffer.c | 463 ----- drivers/misc/habanalabs/command_submission.c | 1232 ------------- drivers/misc/habanalabs/common/Makefile | 9 + drivers/misc/habanalabs/common/asid.c | 57 + drivers/misc/habanalabs/common/command_buffer.c | 463 +++++ .../misc/habanalabs/common/command_submission.c | 1232 +++++++++++++ drivers/misc/habanalabs/common/context.c | 237 +++ drivers/misc/habanalabs/common/debugfs.c | 1411 ++++++++++++++ drivers/misc/habanalabs/common/device.c | 1506 +++++++++++++++ drivers/misc/habanalabs/common/firmware_if.c | 589 ++++++ drivers/misc/habanalabs/common/habanalabs.h | 1948 ++++++++++++++++++++ drivers/misc/habanalabs/common/habanalabs_drv.c | 529 ++++++ drivers/misc/habanalabs/common/habanalabs_ioctl.c | 546 ++++++ drivers/misc/habanalabs/common/hw_queue.c | 918 +++++++++ drivers/misc/habanalabs/common/hwmon.c | 579 ++++++ drivers/misc/habanalabs/common/irq.c | 320 ++++ drivers/misc/habanalabs/common/memory.c | 1843 ++++++++++++++++++ drivers/misc/habanalabs/common/mmu.c | 1037 +++++++++++ drivers/misc/habanalabs/common/pci.c | 400 ++++ drivers/misc/habanalabs/common/sysfs.c | 442 +++++ drivers/misc/habanalabs/context.c | 237 --- drivers/misc/habanalabs/debugfs.c | 1411 -------------- drivers/misc/habanalabs/device.c | 1506 --------------- drivers/misc/habanalabs/firmware_if.c | 589 ------ drivers/misc/habanalabs/gaudi/Makefile | 2 +- drivers/misc/habanalabs/gaudi/gaudiP.h | 2 +- drivers/misc/habanalabs/goya/goyaP.h | 2 +- drivers/misc/habanalabs/habanalabs.h | 1948 -------------------- drivers/misc/habanalabs/habanalabs_drv.c | 529 ------ drivers/misc/habanalabs/habanalabs_ioctl.c | 546 ------ drivers/misc/habanalabs/hw_queue.c | 918 --------- drivers/misc/habanalabs/hwmon.c | 579 ------ drivers/misc/habanalabs/include/armcp_if.h | 407 ---- drivers/misc/habanalabs/include/common/armcp_if.h | 407 ++++ .../misc/habanalabs/include/common/hl_boot_if.h | 98 + drivers/misc/habanalabs/include/common/qman_if.h | 68 + drivers/misc/habanalabs/include/hl_boot_if.h | 98 - drivers/misc/habanalabs/include/qman_if.h | 68 - drivers/misc/habanalabs/irq.c | 320 ---- drivers/misc/habanalabs/memory.c | 1843 ------------------ drivers/misc/habanalabs/mmu.c | 1037 ----------- drivers/misc/habanalabs/pci.c | 400 ---- drivers/misc/habanalabs/sysfs.c | 442 ----- 45 files changed, 14647 insertions(+), 14639 deletions(-) delete mode 100644 drivers/misc/habanalabs/asid.c delete mode 100644 drivers/misc/habanalabs/command_buffer.c delete mode 100644 drivers/misc/habanalabs/command_submission.c create mode 100644 drivers/misc/habanalabs/common/Makefile create mode 100644 drivers/misc/habanalabs/common/asid.c create mode 100644 drivers/misc/habanalabs/common/command_buffer.c create mode 100644 drivers/misc/habanalabs/common/command_submission.c create mode 100644 drivers/misc/habanalabs/common/context.c create mode 100644 drivers/misc/habanalabs/common/debugfs.c create mode 100644 drivers/misc/habanalabs/common/device.c create mode 100644 drivers/misc/habanalabs/common/firmware_if.c create mode 100644 drivers/misc/habanalabs/common/habanalabs.h create mode 100644 drivers/misc/habanalabs/common/habanalabs_drv.c create mode 100644 drivers/misc/habanalabs/common/habanalabs_ioctl.c create mode 100644 drivers/misc/habanalabs/common/hw_queue.c create mode 100644 drivers/misc/habanalabs/common/hwmon.c create mode 100644 drivers/misc/habanalabs/common/irq.c create mode 100644 drivers/misc/habanalabs/common/memory.c create mode 100644 drivers/misc/habanalabs/common/mmu.c create mode 100644 drivers/misc/habanalabs/common/pci.c create mode 100644 drivers/misc/habanalabs/common/sysfs.c delete mode 100644 drivers/misc/habanalabs/context.c delete mode 100644 drivers/misc/habanalabs/debugfs.c delete mode 100644 drivers/misc/habanalabs/device.c delete mode 100644 drivers/misc/habanalabs/firmware_if.c delete mode 100644 drivers/misc/habanalabs/habanalabs.h delete mode 100644 drivers/misc/habanalabs/habanalabs_drv.c delete mode 100644 drivers/misc/habanalabs/habanalabs_ioctl.c delete mode 100644 drivers/misc/habanalabs/hw_queue.c delete mode 100644 drivers/misc/habanalabs/hwmon.c delete mode 100644 drivers/misc/habanalabs/include/armcp_if.h create mode 100644 drivers/misc/habanalabs/include/common/armcp_if.h create mode 100644 drivers/misc/habanalabs/include/common/hl_boot_if.h create mode 100644 drivers/misc/habanalabs/include/common/qman_if.h delete mode 100644 drivers/misc/habanalabs/include/hl_boot_if.h delete mode 100644 drivers/misc/habanalabs/include/qman_if.h delete mode 100644 drivers/misc/habanalabs/irq.c delete mode 100644 drivers/misc/habanalabs/memory.c delete mode 100644 drivers/misc/habanalabs/mmu.c delete mode 100644 drivers/misc/habanalabs/pci.c delete mode 100644 drivers/misc/habanalabs/sysfs.c diff --git a/drivers/misc/habanalabs/Makefile b/drivers/misc/habanalabs/Makefile index 421ebd903069..a786c0a7de9a 100644 --- a/drivers/misc/habanalabs/Makefile +++ b/drivers/misc/habanalabs/Makefile @@ -3,16 +3,15 @@ # Makefile for HabanaLabs AI accelerators driver # -obj-m := habanalabs.o +obj-$(CONFIG_HABANA_AI) := habanalabs.o -habanalabs-y := habanalabs_drv.o device.o context.o asid.o habanalabs_ioctl.o \ - command_buffer.o hw_queue.o irq.o sysfs.o hwmon.o memory.o \ - command_submission.o mmu.o firmware_if.o pci.o - -habanalabs-$(CONFIG_DEBUG_FS) += debugfs.o +include $(src)/common/Makefile +habanalabs-y += $(HL_COMMON_FILES) include $(src)/goya/Makefile habanalabs-y += $(HL_GOYA_FILES) include $(src)/gaudi/Makefile habanalabs-y += $(HL_GAUDI_FILES) + +habanalabs-$(CONFIG_DEBUG_FS) += common/debugfs.o diff --git a/drivers/misc/habanalabs/asid.c b/drivers/misc/habanalabs/asid.c deleted file mode 100644 index a2fdf31cf27c..000000000000 --- a/drivers/misc/habanalabs/asid.c +++ /dev/null @@ -1,57 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -/* - * Copyright 2016-2019 HabanaLabs, Ltd. - * All Rights Reserved. - */ - -#include "habanalabs.h" - -#include - -int hl_asid_init(struct hl_device *hdev) -{ - hdev->asid_bitmap = kcalloc(BITS_TO_LONGS(hdev->asic_prop.max_asid), - sizeof(*hdev->asid_bitmap), GFP_KERNEL); - if (!hdev->asid_bitmap) - return -ENOMEM; - - mutex_init(&hdev->asid_mutex); - - /* ASID 0 is reserved for the kernel driver and device CPU */ - set_bit(0, hdev->asid_bitmap); - - return 0; -} - -void hl_asid_fini(struct hl_device *hdev) -{ - mutex_destroy(&hdev->asid_mutex); - kfree(hdev->asid_bitmap); -} - -unsigned long hl_asid_alloc(struct hl_device *hdev) -{ - unsigned long found; - - mutex_lock(&hdev->asid_mutex); - - found = find_first_zero_bit(hdev->asid_bitmap, - hdev->asic_prop.max_asid); - if (found == hdev->asic_prop.max_asid) - found = 0; - else - set_bit(found, hdev->asid_bitmap); - - mutex_unlock(&hdev->asid_mutex); - - return found; -} - -void hl_asid_free(struct hl_device *hdev, unsigned long asid) -{ - if (WARN((asid == 0 || asid >= hdev->asic_prop.max_asid), - "Invalid ASID %lu", asid)) - return; - clear_bit(asid, hdev->asid_bitmap); -} diff --git a/drivers/misc/habanalabs/command_buffer.c b/drivers/misc/habanalabs/command_buffer.c deleted file mode 100644 index 02d13f71b1df..000000000000 --- a/drivers/misc/habanalabs/command_buffer.c +++ /dev/null @@ -1,463 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -/* - * Copyright 2016-2019 HabanaLabs, Ltd. - * All Rights Reserved. - */ - -#include -#include "habanalabs.h" - -#include -#include - -static void cb_fini(struct hl_device *hdev, struct hl_cb *cb) -{ - hdev->asic_funcs->asic_dma_free_coherent(hdev, cb->size, - (void *) (uintptr_t) cb->kernel_address, - cb->bus_address); - kfree(cb); -} - -static void cb_do_release(struct hl_device *hdev, struct hl_cb *cb) -{ - if (cb->is_pool) { - spin_lock(&hdev->cb_pool_lock); - list_add(&cb->pool_list, &hdev->cb_pool); - spin_unlock(&hdev->cb_pool_lock); - } else { - cb_fini(hdev, cb); - } -} - -static void cb_release(struct kref *ref) -{ - struct hl_device *hdev; - struct hl_cb *cb; - - cb = container_of(ref, struct hl_cb, refcount); - hdev = cb->hdev; - - hl_debugfs_remove_cb(cb); - - cb_do_release(hdev, cb); -} - -static struct hl_cb *hl_cb_alloc(struct hl_device *hdev, u32 cb_size, - int ctx_id) -{ - struct hl_cb *cb; - void *p; - - /* - * We use of GFP_ATOMIC here because this function can be called from - * the latency-sensitive code path for command submission. Due to H/W - * limitations in some of the ASICs, the kernel must copy the user CB - * that is designated for an external queue and actually enqueue - * the kernel's copy. Hence, we must never sleep in this code section - * and must use GFP_ATOMIC for all memory allocations. - */ - if (ctx_id == HL_KERNEL_ASID_ID) - cb = kzalloc(sizeof(*cb), GFP_ATOMIC); - else - cb = kzalloc(sizeof(*cb), GFP_KERNEL); - - if (!cb) - return NULL; - - if (ctx_id == HL_KERNEL_ASID_ID) - p = hdev->asic_funcs->asic_dma_alloc_coherent(hdev, cb_size, - &cb->bus_address, GFP_ATOMIC); - else - p = hdev->asic_funcs->asic_dma_alloc_coherent(hdev, cb_size, - &cb->bus_address, - GFP_USER | __GFP_ZERO); - if (!p) { - dev_err(hdev->dev, - "failed to allocate %d of dma memory for CB\n", - cb_size); - kfree(cb); - return NULL; - } - - cb->kernel_address = (u64) (uintptr_t) p; - cb->size = cb_size; - - return cb; -} - -int hl_cb_create(struct hl_device *hdev, struct hl_cb_mgr *mgr, - u32 cb_size, u64 *handle, int ctx_id) -{ - struct hl_cb *cb; - bool alloc_new_cb = true; - int rc; - - /* - * Can't use generic function to check this because of special case - * where we create a CB as part of the reset process - */ - if ((hdev->disabled) || ((atomic_read(&hdev->in_reset)) && - (ctx_id != HL_KERNEL_ASID_ID))) { - dev_warn_ratelimited(hdev->dev, - "Device is disabled or in reset. Can't create new CBs\n"); - rc = -EBUSY; - goto out_err; - } - - if (cb_size > SZ_2M) { - dev_err(hdev->dev, "CB size %d must be less than %d\n", - cb_size, SZ_2M); - rc = -EINVAL; - goto out_err; - } - - /* Minimum allocation must be PAGE SIZE */ - if (cb_size < PAGE_SIZE) - cb_size = PAGE_SIZE; - - if (ctx_id == HL_KERNEL_ASID_ID && - cb_size <= hdev->asic_prop.cb_pool_cb_size) { - - spin_lock(&hdev->cb_pool_lock); - if (!list_empty(&hdev->cb_pool)) { - cb = list_first_entry(&hdev->cb_pool, typeof(*cb), - pool_list); - list_del(&cb->pool_list); - spin_unlock(&hdev->cb_pool_lock); - alloc_new_cb = false; - } else { - spin_unlock(&hdev->cb_pool_lock); - dev_dbg(hdev->dev, "CB pool is empty\n"); - } - } - - if (alloc_new_cb) { - cb = hl_cb_alloc(hdev, cb_size, ctx_id); - if (!cb) { - rc = -ENOMEM; - goto out_err; - } - } - - cb->hdev = hdev; - cb->ctx_id = ctx_id; - - spin_lock(&mgr->cb_lock); - rc = idr_alloc(&mgr->cb_handles, cb, 1, 0, GFP_ATOMIC); - spin_unlock(&mgr->cb_lock); - - if (rc < 0) { - dev_err(hdev->dev, "Failed to allocate IDR for a new CB\n"); - goto release_cb; - } - - cb->id = rc; - - kref_init(&cb->refcount); - spin_lock_init(&cb->lock); - - /* - * idr is 32-bit so we can safely OR it with a mask that is above - * 32 bit - */ - *handle = cb->id | HL_MMAP_CB_MASK; - *handle <<= PAGE_SHIFT; - - hl_debugfs_add_cb(cb); - - return 0; - -release_cb: - cb_do_release(hdev, cb); -out_err: - *handle = 0; - - return rc; -} - -int hl_cb_destroy(struct hl_device *hdev, struct hl_cb_mgr *mgr, u64 cb_handle) -{ - struct hl_cb *cb; - u32 handle; - int rc = 0; - - /* - * handle was given to user to do mmap, I need to shift it back to - * how the idr module gave it to me - */ - cb_handle >>= PAGE_SHIFT; - handle = (u32) cb_handle; - - spin_lock(&mgr->cb_lock); - - cb = idr_find(&mgr->cb_handles, handle); - if (cb) { - idr_remove(&mgr->cb_handles, handle); - spin_unlock(&mgr->cb_lock); - kref_put(&cb->refcount, cb_release); - } else { - spin_unlock(&mgr->cb_lock); - dev_err(hdev->dev, - "CB destroy failed, no match to handle 0x%x\n", handle); - rc = -EINVAL; - } - - return rc; -} - -int hl_cb_ioctl(struct hl_fpriv *hpriv, void *data) -{ - union hl_cb_args *args = data; - struct hl_device *hdev = hpriv->hdev; - u64 handle = 0; - int rc; - - if (hl_device_disabled_or_in_reset(hdev)) { - dev_warn_ratelimited(hdev->dev, - "Device is %s. Can't execute CB IOCTL\n", - atomic_read(&hdev->in_reset) ? "in_reset" : "disabled"); - return -EBUSY; - } - - switch (args->in.op) { - case HL_CB_OP_CREATE: - if (args->in.cb_size > HL_MAX_CB_SIZE) { - dev_err(hdev->dev, - "User requested CB size %d must be less than %d\n", - args->in.cb_size, HL_MAX_CB_SIZE); - rc = -EINVAL; - } else { - rc = hl_cb_create(hdev, &hpriv->cb_mgr, - args->in.cb_size, &handle, - hpriv->ctx->asid); - } - - memset(args, 0, sizeof(*args)); - args->out.cb_handle = handle; - break; - - case HL_CB_OP_DESTROY: - rc = hl_cb_destroy(hdev, &hpriv->cb_mgr, - args->in.cb_handle); - break; - - default: - rc = -ENOTTY; - break; - } - - return rc; -} - -static void cb_vm_close(struct vm_area_struct *vma) -{ - struct hl_cb *cb = (struct hl_cb *) vma->vm_private_data; - long new_mmap_size; - - new_mmap_size = cb->mmap_size - (vma->vm_end - vma->vm_start); - - if (new_mmap_size > 0) { - cb->mmap_size = new_mmap_size; - return; - } - - spin_lock(&cb->lock); - cb->mmap = false; - spin_unlock(&cb->lock); - - hl_cb_put(cb); - vma->vm_private_data = NULL; -} - -static const struct vm_operations_struct cb_vm_ops = { - .close = cb_vm_close -}; - -int hl_cb_mmap(struct hl_fpriv *hpriv, struct vm_area_struct *vma) -{ - struct hl_device *hdev = hpriv->hdev; - struct hl_cb *cb; - phys_addr_t address; - u32 handle; - int rc; - - handle = vma->vm_pgoff; - - /* reference was taken here */ - cb = hl_cb_get(hdev, &hpriv->cb_mgr, handle); - if (!cb) { - dev_err(hdev->dev, - "CB mmap failed, no match to handle 0x%x\n", handle); - return -EINVAL; - } - - /* Validation check */ - if ((vma->vm_end - vma->vm_start) != ALIGN(cb->size, PAGE_SIZE)) { - dev_err(hdev->dev, - "CB mmap failed, mmap size 0x%lx != 0x%x cb size\n", - vma->vm_end - vma->vm_start, cb->size); - rc = -EINVAL; - goto put_cb; - } - - spin_lock(&cb->lock); - - if (cb->mmap) { - dev_err(hdev->dev, - "CB mmap failed, CB already mmaped to user\n"); - rc = -EINVAL; - goto release_lock; - } - - cb->mmap = true; - - spin_unlock(&cb->lock); - - vma->vm_ops = &cb_vm_ops; - - /* - * Note: We're transferring the cb reference to - * vma->vm_private_data here. - */ - - vma->vm_private_data = cb; - - /* Calculate address for CB */ - address = virt_to_phys((void *) (uintptr_t) cb->kernel_address); - - rc = hdev->asic_funcs->cb_mmap(hdev, vma, cb->kernel_address, - address, cb->size); - - if (rc) { - spin_lock(&cb->lock); - cb->mmap = false; - goto release_lock; - } - - cb->mmap_size = cb->size; - - return 0; - -release_lock: - spin_unlock(&cb->lock); -put_cb: - hl_cb_put(cb); - return rc; -} - -struct hl_cb *hl_cb_get(struct hl_device *hdev, struct hl_cb_mgr *mgr, - u32 handle) -{ - struct hl_cb *cb; - - spin_lock(&mgr->cb_lock); - cb = idr_find(&mgr->cb_handles, handle); - - if (!cb) { - spin_unlock(&mgr->cb_lock); - dev_warn(hdev->dev, - "CB get failed, no match to handle 0x%x\n", handle); - return NULL; - } - - kref_get(&cb->refcount); - - spin_unlock(&mgr->cb_lock); - - return cb; - -} - -void hl_cb_put(struct hl_cb *cb) -{ - kref_put(&cb->refcount, cb_release); -} - -void hl_cb_mgr_init(struct hl_cb_mgr *mgr) -{ - spin_lock_init(&mgr->cb_lock); - idr_init(&mgr->cb_handles); -} - -void hl_cb_mgr_fini(struct hl_device *hdev, struct hl_cb_mgr *mgr) -{ - struct hl_cb *cb; - struct idr *idp; - u32 id; - - idp = &mgr->cb_handles; - - idr_for_each_entry(idp, cb, id) { - if (kref_put(&cb->refcount, cb_release) != 1) - dev_err(hdev->dev, - "CB %d for CTX ID %d is still alive\n", - id, cb->ctx_id); - } - - idr_destroy(&mgr->cb_handles); -} - -struct hl_cb *hl_cb_kernel_create(struct hl_device *hdev, u32 cb_size) -{ - u64 cb_handle; - struct hl_cb *cb; - int rc; - - rc = hl_cb_create(hdev, &hdev->kernel_cb_mgr, cb_size, &cb_handle, - HL_KERNEL_ASID_ID); - if (rc) { - dev_err(hdev->dev, - "Failed to allocate CB for the kernel driver %d\n", rc); - return NULL; - } - - cb_handle >>= PAGE_SHIFT; - cb = hl_cb_get(hdev, &hdev->kernel_cb_mgr, (u32) cb_handle); - /* hl_cb_get should never fail here so use kernel WARN */ - WARN(!cb, "Kernel CB handle invalid 0x%x\n", (u32) cb_handle); - if (!cb) - goto destroy_cb; - - return cb; - -destroy_cb: - hl_cb_destroy(hdev, &hdev->kernel_cb_mgr, cb_handle << PAGE_SHIFT); - - return NULL; -} - -int hl_cb_pool_init(struct hl_device *hdev) -{ - struct hl_cb *cb; - int i; - - INIT_LIST_HEAD(&hdev->cb_pool); - spin_lock_init(&hdev->cb_pool_lock); - - for (i = 0 ; i < hdev->asic_prop.cb_pool_cb_cnt ; i++) { - cb = hl_cb_alloc(hdev, hdev->asic_prop.cb_pool_cb_size, - HL_KERNEL_ASID_ID); - if (cb) { - cb->is_pool = true; - list_add(&cb->pool_list, &hdev->cb_pool); - } else { - hl_cb_pool_fini(hdev); - return -ENOMEM; - } - } - - return 0; -} - -int hl_cb_pool_fini(struct hl_device *hdev) -{ - struct hl_cb *cb, *tmp; - - list_for_each_entry_safe(cb, tmp, &hdev->cb_pool, pool_list) { - list_del(&cb->pool_list); - cb_fini(hdev, cb); - } - - return 0; -} diff --git a/drivers/misc/habanalabs/command_submission.c b/drivers/misc/habanalabs/command_submission.c deleted file mode 100644 index c605be89f764..000000000000 --- a/drivers/misc/habanalabs/command_submission.c +++ /dev/null @@ -1,1232 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -/* - * Copyright 2016-2019 HabanaLabs, Ltd. - * All Rights Reserved. - */ - -#include -#include "habanalabs.h" - -#include -#include - -#define HL_CS_FLAGS_SIG_WAIT (HL_CS_FLAGS_SIGNAL | HL_CS_FLAGS_WAIT) - -static void job_wq_completion(struct work_struct *work); -static long _hl_cs_wait_ioctl(struct hl_device *hdev, - struct hl_ctx *ctx, u64 timeout_us, u64 seq); -static void cs_do_release(struct kref *ref); - -static void hl_sob_reset(struct kref *ref) -{ - struct hl_hw_sob *hw_sob = container_of(ref, struct hl_hw_sob, - kref); - struct hl_device *hdev = hw_sob->hdev; - - hdev->asic_funcs->reset_sob(hdev, hw_sob); -} - -void hl_sob_reset_error(struct kref *ref) -{ - struct hl_hw_sob *hw_sob = container_of(ref, struct hl_hw_sob, - kref); - struct hl_device *hdev = hw_sob->hdev; - - dev_crit(hdev->dev, - "SOB release shouldn't be called here, q_idx: %d, sob_id: %d\n", - hw_sob->q_idx, hw_sob->sob_id); -} - -static const char *hl_fence_get_driver_name(struct dma_fence *fence) -{ - return "HabanaLabs"; -} - -static const char *hl_fence_get_timeline_name(struct dma_fence *fence) -{ - struct hl_cs_compl *hl_cs_compl = - container_of(fence, struct hl_cs_compl, base_fence); - - return dev_name(hl_cs_compl->hdev->dev); -} - -static bool hl_fence_enable_signaling(struct dma_fence *fence) -{ - return true; -} - -static void hl_fence_release(struct dma_fence *fence) -{ - struct hl_cs_compl *hl_cs_cmpl = - container_of(fence, struct hl_cs_compl, base_fence); - struct hl_device *hdev = hl_cs_cmpl->hdev; - - /* EBUSY means the CS was never submitted and hence we don't have - * an attached hw_sob object that we should handle here - */ - if (fence->error == -EBUSY) - goto free; - - if ((hl_cs_cmpl->type == CS_TYPE_SIGNAL) || - (hl_cs_cmpl->type == CS_TYPE_WAIT)) { - - dev_dbg(hdev->dev, - "CS 0x%llx type %d finished, sob_id: %d, sob_val: 0x%x\n", - hl_cs_cmpl->cs_seq, - hl_cs_cmpl->type, - hl_cs_cmpl->hw_sob->sob_id, - hl_cs_cmpl->sob_val); - - /* - * A signal CS can get completion while the corresponding wait - * for signal CS is on its way to the PQ. The wait for signal CS - * will get stuck if the signal CS incremented the SOB to its - * max value and there are no pending (submitted) waits on this - * SOB. - * We do the following to void this situation: - * 1. The wait for signal CS must get a ref for the signal CS as - * soon as possible in cs_ioctl_signal_wait() and put it - * before being submitted to the PQ but after it incremented - * the SOB refcnt in init_signal_wait_cs(). - * 2. Signal/Wait for signal CS will decrement the SOB refcnt - * here. - * These two measures guarantee that the wait for signal CS will - * reset the SOB upon completion rather than the signal CS and - * hence the above scenario is avoided. - */ - kref_put(&hl_cs_cmpl->hw_sob->kref, hl_sob_reset); - } - -free: - kfree_rcu(hl_cs_cmpl, base_fence.rcu); -} - -static const struct dma_fence_ops hl_fence_ops = { - .get_driver_name = hl_fence_get_driver_name, - .get_timeline_name = hl_fence_get_timeline_name, - .enable_signaling = hl_fence_enable_signaling, - .release = hl_fence_release -}; - -static void cs_get(struct hl_cs *cs) -{ - kref_get(&cs->refcount); -} - -static int cs_get_unless_zero(struct hl_cs *cs) -{ - return kref_get_unless_zero(&cs->refcount); -} - -static void cs_put(struct hl_cs *cs) -{ - kref_put(&cs->refcount, cs_do_release); -} - -static bool is_cb_patched(struct hl_device *hdev, struct hl_cs_job *job) -{ - /* - * Patched CB is created for external queues jobs, and for H/W queues - * jobs if the user CB was allocated by driver and MMU is disabled. - */ - return (job->queue_type == QUEUE_TYPE_EXT || - (job->queue_type == QUEUE_TYPE_HW && - job->is_kernel_allocated_cb && - !hdev->mmu_enable)); -} - -/* - * cs_parser - parse the user command submission - * - * @hpriv : pointer to the private data of the fd - * @job : pointer to the job that holds the command submission info - * - * The function parses the command submission of the user. It calls the - * ASIC specific parser, which returns a list of memory blocks to send - * to the device as different command buffers - * - */ -static int cs_parser(struct hl_fpriv *hpriv, struct hl_cs_job *job) -{ - struct hl_device *hdev = hpriv->hdev; - struct hl_cs_parser parser; - int rc; - - parser.ctx_id = job->cs->ctx->asid; - parser.cs_sequence = job->cs->sequence; - parser.job_id = job->id; - - parser.hw_queue_id = job->hw_queue_id; - parser.job_userptr_list = &job->userptr_list; - parser.patched_cb = NULL; - parser.user_cb = job->user_cb; - parser.user_cb_size = job->user_cb_size; - parser.queue_type = job->queue_type; - parser.is_kernel_allocated_cb = job->is_kernel_allocated_cb; - job->patched_cb = NULL; - - rc = hdev->asic_funcs->cs_parser(hdev, &parser); - - if (is_cb_patched(hdev, job)) { - if (!rc) { - job->patched_cb = parser.patched_cb; - job->job_cb_size = parser.patched_cb_size; - job->contains_dma_pkt = parser.contains_dma_pkt; - - spin_lock(&job->patched_cb->lock); - job->patched_cb->cs_cnt++; - spin_unlock(&job->patched_cb->lock); - } - - /* - * Whether the parsing worked or not, we don't need the - * original CB anymore because it was already parsed and - * won't be accessed again for this CS - */ - spin_lock(&job->user_cb->lock); - job->user_cb->cs_cnt--; - spin_unlock(&job->user_cb->lock); - hl_cb_put(job->user_cb); - job->user_cb = NULL; - } else if (!rc) { - job->job_cb_size = job->user_cb_size; - } - - return rc; -} - -static void free_job(struct hl_device *hdev, struct hl_cs_job *job) -{ - struct hl_cs *cs = job->cs; - - if (is_cb_patched(hdev, job)) { - hl_userptr_delete_list(hdev, &job->userptr_list); - - /* - * We might arrive here from rollback and patched CB wasn't - * created, so we need to check it's not NULL - */ - if (job->patched_cb) { - spin_lock(&job->patched_cb->lock); - job->patched_cb->cs_cnt--; - spin_unlock(&job->patched_cb->lock); - - hl_cb_put(job->patched_cb); - } - } - - /* For H/W queue jobs, if a user CB was allocated by driver and MMU is - * enabled, the user CB isn't released in cs_parser() and thus should be - * released here. - */ - if (job->queue_type == QUEUE_TYPE_HW && - job->is_kernel_allocated_cb && hdev->mmu_enable) { - spin_lock(&job->user_cb->lock); - job->user_cb->cs_cnt--; - spin_unlock(&job->user_cb->lock); - - hl_cb_put(job->user_cb); - } - - /* - * This is the only place where there can be multiple threads - * modifying the list at the same time - */ - spin_lock(&cs->job_lock); - list_del(&job->cs_node); - spin_unlock(&cs->job_lock); - - hl_debugfs_remove_job(hdev, job); - - if (job->queue_type == QUEUE_TYPE_EXT || - job->queue_type == QUEUE_TYPE_HW) - cs_put(cs); - - kfree(job); -} - -static void cs_counters_aggregate(struct hl_device *hdev, struct hl_ctx *ctx) -{ - hdev->aggregated_cs_counters.device_in_reset_drop_cnt += - ctx->cs_counters.device_in_reset_drop_cnt; - hdev->aggregated_cs_counters.out_of_mem_drop_cnt += - ctx->cs_counters.out_of_mem_drop_cnt; - hdev->aggregated_cs_counters.parsing_drop_cnt += - ctx->cs_counters.parsing_drop_cnt; - hdev->aggregated_cs_counters.queue_full_drop_cnt += - ctx->cs_counters.queue_full_drop_cnt; -} - -static void cs_do_release(struct kref *ref) -{ - struct hl_cs *cs = container_of(ref, struct hl_cs, - refcount); - struct hl_device *hdev = cs->ctx->hdev; - struct hl_cs_job *job, *tmp; - - cs->completed = true; - - /* - * Although if we reached here it means that all external jobs have - * finished, because each one of them took refcnt to CS, we still - * need to go over the internal jobs and free them. Otherwise, we - * will have leaked memory and what's worse, the CS object (and - * potentially the CTX object) could be released, while the JOB - * still holds a pointer to them (but no reference). - */ - list_for_each_entry_safe(job, tmp, &cs->job_list, cs_node) - free_job(hdev, job); - - /* We also need to update CI for internal queues */ - if (cs->submitted) { - hdev->asic_funcs->hw_queues_lock(hdev); - - hdev->cs_active_cnt--; - if (!hdev->cs_active_cnt) { - struct hl_device_idle_busy_ts *ts; - - ts = &hdev->idle_busy_ts_arr[hdev->idle_busy_ts_idx++]; - ts->busy_to_idle_ts = ktime_get(); - - if (hdev->idle_busy_ts_idx == HL_IDLE_BUSY_TS_ARR_SIZE) - hdev->idle_busy_ts_idx = 0; - } else if (hdev->cs_active_cnt < 0) { - dev_crit(hdev->dev, "CS active cnt %d is negative\n", - hdev->cs_active_cnt); - } - - hdev->asic_funcs->hw_queues_unlock(hdev); - - hl_int_hw_queue_update_ci(cs); - - spin_lock(&hdev->hw_queues_mirror_lock); - /* remove CS from hw_queues mirror list */ - list_del_init(&cs->mirror_node); - spin_unlock(&hdev->hw_queues_mirror_lock); - - /* - * Don't cancel TDR in case this CS was timedout because we - * might be running from the TDR context - */ - if ((!cs->timedout) && - (hdev->timeout_jiffies != MAX_SCHEDULE_TIMEOUT)) { - struct hl_cs *next; - - if (cs->tdr_active) - cancel_delayed_work_sync(&cs->work_tdr); - - spin_lock(&hdev->hw_queues_mirror_lock); - - /* queue TDR for next CS */ - next = list_first_entry_or_null( - &hdev->hw_queues_mirror_list, - struct hl_cs, mirror_node); - - if ((next) && (!next->tdr_active)) { - next->tdr_active = true; - schedule_delayed_work(&next->work_tdr, - hdev->timeout_jiffies); - } - - spin_unlock(&hdev->hw_queues_mirror_lock); - } - } else if (cs->type == CS_TYPE_WAIT) { - /* - * In case the wait for signal CS was submitted, the put occurs - * in init_signal_wait_cs() right before hanging on the PQ. - */ - dma_fence_put(cs->signal_fence); - } - - /* - * Must be called before hl_ctx_put because inside we use ctx to get - * the device - */ - hl_debugfs_remove_cs(cs); - - hl_ctx_put(cs->ctx); - - /* We need to mark an error for not submitted because in that case - * the dma fence release flow is different. Mainly, we don't need - * to handle hw_sob for signal/wait - */ - if (cs->timedout) - dma_fence_set_error(cs->fence, -ETIMEDOUT); - else if (cs->aborted) - dma_fence_set_error(cs->fence, -EIO); - else if (!cs->submitted) - dma_fence_set_error(cs->fence, -EBUSY); - - dma_fence_signal(cs->fence); - dma_fence_put(cs->fence); - - cs_counters_aggregate(hdev, cs->ctx); - - kfree(cs->jobs_in_queue_cnt); - kfree(cs); -} - -static void cs_timedout(struct work_struct *work) -{ - struct hl_device *hdev; - int ctx_asid, rc; - struct hl_cs *cs = container_of(work, struct hl_cs, - work_tdr.work); - rc = cs_get_unless_zero(cs); - if (!rc) - return; - - if ((!cs->submitted) || (cs->completed)) { - cs_put(cs); - return; - } - - /* Mark the CS is timed out so we won't try to cancel its TDR */ - cs->timedout = true; - - hdev = cs->ctx->hdev; - ctx_asid = cs->ctx->asid; - - dev_err(hdev->dev, - "Command submission %llu has not finished in time!\n", - cs->sequence); - - cs_put(cs); - - if (hdev->reset_on_lockup) - hl_device_reset(hdev, false, false); -} - -static int allocate_cs(struct hl_device *hdev, struct hl_ctx *ctx, - enum hl_cs_type cs_type, struct hl_cs **cs_new) -{ - struct hl_cs_compl *cs_cmpl; - struct dma_fence *other = NULL; - struct hl_cs *cs; - int rc; - - cs = kzalloc(sizeof(*cs), GFP_ATOMIC); - if (!cs) - return -ENOMEM; - - cs->ctx = ctx; - cs->submitted = false; - cs->completed = false; - cs->type = cs_type; - INIT_LIST_HEAD(&cs->job_list); - INIT_DELAYED_WORK(&cs->work_tdr, cs_timedout); - kref_init(&cs->refcount); - spin_lock_init(&cs->job_lock); - - cs_cmpl = kmalloc(sizeof(*cs_cmpl), GFP_ATOMIC); - if (!cs_cmpl) { - rc = -ENOMEM; - goto free_cs; - } - - cs_cmpl->hdev = hdev; - cs_cmpl->type = cs->type; - spin_lock_init(&cs_cmpl->lock); - cs->fence = &cs_cmpl->base_fence; - - spin_lock(&ctx->cs_lock); - - cs_cmpl->cs_seq = ctx->cs_sequence; - other = ctx->cs_pending[cs_cmpl->cs_seq & - (hdev->asic_prop.max_pending_cs - 1)]; - if ((other) && (!dma_fence_is_signaled(other))) { - dev_dbg(hdev->dev, - "Rejecting CS because of too many in-flights CS\n"); - rc = -EAGAIN; - goto free_fence; - } - - cs->jobs_in_queue_cnt = kcalloc(hdev->asic_prop.max_queues, - sizeof(*cs->jobs_in_queue_cnt), GFP_ATOMIC); - if (!cs->jobs_in_queue_cnt) { - rc = -ENOMEM; - goto free_fence; - } - - dma_fence_init(&cs_cmpl->base_fence, &hl_fence_ops, &cs_cmpl->lock, - ctx->asid, ctx->cs_sequence); - - cs->sequence = cs_cmpl->cs_seq; - - ctx->cs_pending[cs_cmpl->cs_seq & - (hdev->asic_prop.max_pending_cs - 1)] = - &cs_cmpl->base_fence; - ctx->cs_sequence++; - - dma_fence_get(&cs_cmpl->base_fence); - - dma_fence_put(other); - - spin_unlock(&ctx->cs_lock); - - *cs_new = cs; - - return 0; - -free_fence: - spin_unlock(&ctx->cs_lock); - kfree(cs_cmpl); -free_cs: - kfree(cs); - return rc; -} - -static void cs_rollback(struct hl_device *hdev, struct hl_cs *cs) -{ - struct hl_cs_job *job, *tmp; - - list_for_each_entry_safe(job, tmp, &cs->job_list, cs_node) - free_job(hdev, job); -} - -void hl_cs_rollback_all(struct hl_device *hdev) -{ - int i; - struct hl_cs *cs, *tmp; - - /* flush all completions */ - for (i = 0 ; i < hdev->asic_prop.completion_queues_count ; i++) - flush_workqueue(hdev->cq_wq[i]); - - /* Make sure we don't have leftovers in the H/W queues mirror list */ - list_for_each_entry_safe(cs, tmp, &hdev->hw_queues_mirror_list, - mirror_node) { - cs_get(cs); - cs->aborted = true; - dev_warn_ratelimited(hdev->dev, "Killing CS %d.%llu\n", - cs->ctx->asid, cs->sequence); - cs_rollback(hdev, cs); - cs_put(cs); - } -} - -static void job_wq_completion(struct work_struct *work) -{ - struct hl_cs_job *job = container_of(work, struct hl_cs_job, - finish_work); - struct hl_cs *cs = job->cs; - struct hl_device *hdev = cs->ctx->hdev; - - /* job is no longer needed */ - free_job(hdev, job); -} - -static int validate_queue_index(struct hl_device *hdev, - struct hl_cs_chunk *chunk, - enum hl_queue_type *queue_type, - bool *is_kernel_allocated_cb) -{ - struct asic_fixed_properties *asic = &hdev->asic_prop; - struct hw_queue_properties *hw_queue_prop; - - /* This must be checked here to prevent out-of-bounds access to - * hw_queues_props array - */ - if (chunk->queue_index >= asic->max_queues) { - dev_err(hdev->dev, "Queue index %d is invalid\n", - chunk->queue_index); - return -EINVAL; - } - - hw_queue_prop = &asic->hw_queues_props[chunk->queue_index]; - - if (hw_queue_prop->type == QUEUE_TYPE_NA) { - dev_err(hdev->dev, "Queue index %d is invalid\n", - chunk->queue_index); - return -EINVAL; - } - - if (hw_queue_prop->driver_only) { - dev_err(hdev->dev, - "Queue index %d is restricted for the kernel driver\n", - chunk->queue_index); - return -EINVAL; - } - - *queue_type = hw_queue_prop->type; - *is_kernel_allocated_cb = !!hw_queue_prop->requires_kernel_cb; - - return 0; -} - -static struct hl_cb *get_cb_from_cs_chunk(struct hl_device *hdev, - struct hl_cb_mgr *cb_mgr, - struct hl_cs_chunk *chunk) -{ - struct hl_cb *cb; - u32 cb_handle; - - cb_handle = (u32) (chunk->cb_handle >> PAGE_SHIFT); - - cb = hl_cb_get(hdev, cb_mgr, cb_handle); - if (!cb) { - dev_err(hdev->dev, "CB handle 0x%x invalid\n", cb_handle); - return NULL; - } - - if ((chunk->cb_size < 8) || (chunk->cb_size > cb->size)) { - dev_err(hdev->dev, "CB size %u invalid\n", chunk->cb_size); - goto release_cb; - } - - spin_lock(&cb->lock); - cb->cs_cnt++; - spin_unlock(&cb->lock); - - return cb; - -release_cb: - hl_cb_put(cb); - return NULL; -} - -struct hl_cs_job *hl_cs_allocate_job(struct hl_device *hdev, - enum hl_queue_type queue_type, bool is_kernel_allocated_cb) -{ - struct hl_cs_job *job; - - job = kzalloc(sizeof(*job), GFP_ATOMIC); - if (!job) - return NULL; - - job->queue_type = queue_type; - job->is_kernel_allocated_cb = is_kernel_allocated_cb; - - if (is_cb_patched(hdev, job)) - INIT_LIST_HEAD(&job->userptr_list); - - if (job->queue_type == QUEUE_TYPE_EXT) - INIT_WORK(&job->finish_work, job_wq_completion); - - return job; -} - -static int cs_ioctl_default(struct hl_fpriv *hpriv, void __user *chunks, - u32 num_chunks, u64 *cs_seq) -{ - struct hl_device *hdev = hpriv->hdev; - struct hl_cs_chunk *cs_chunk_array; - struct hl_cs_job *job; - struct hl_cs *cs; - struct hl_cb *cb; - bool int_queues_only = true; - u32 size_to_copy; - int rc, i; - - *cs_seq = ULLONG_MAX; - - if (num_chunks > HL_MAX_JOBS_PER_CS) { - dev_err(hdev->dev, - "Number of chunks can NOT be larger than %d\n", - HL_MAX_JOBS_PER_CS); - rc = -EINVAL; - goto out; - } - - cs_chunk_array = kmalloc_array(num_chunks, sizeof(*cs_chunk_array), - GFP_ATOMIC); - if (!cs_chunk_array) { - rc = -ENOMEM; - goto out; - } - - size_to_copy = num_chunks * sizeof(struct hl_cs_chunk); - if (copy_from_user(cs_chunk_array, chunks, size_to_copy)) { - dev_err(hdev->dev, "Failed to copy cs chunk array from user\n"); - rc = -EFAULT; - goto free_cs_chunk_array; - } - - /* increment refcnt for context */ - hl_ctx_get(hdev, hpriv->ctx); - - rc = allocate_cs(hdev, hpriv->ctx, CS_TYPE_DEFAULT, &cs); - if (rc) { - hl_ctx_put(hpriv->ctx); - goto free_cs_chunk_array; - } - - *cs_seq = cs->sequence; - - hl_debugfs_add_cs(cs); - - /* Validate ALL the CS chunks before submitting the CS */ - for (i = 0 ; i < num_chunks ; i++) { - struct hl_cs_chunk *chunk = &cs_chunk_array[i]; - enum hl_queue_type queue_type; - bool is_kernel_allocated_cb; - - rc = validate_queue_index(hdev, chunk, &queue_type, - &is_kernel_allocated_cb); - if (rc) { - hpriv->ctx->cs_counters.parsing_drop_cnt++; - goto free_cs_object; - } - - if (is_kernel_allocated_cb) { - cb = get_cb_from_cs_chunk(hdev, &hpriv->cb_mgr, chunk); - if (!cb) { - hpriv->ctx->cs_counters.parsing_drop_cnt++; - rc = -EINVAL; - goto free_cs_object; - } - } else { - cb = (struct hl_cb *) (uintptr_t) chunk->cb_handle; - } - - if (queue_type == QUEUE_TYPE_EXT || queue_type == QUEUE_TYPE_HW) - int_queues_only = false; - - job = hl_cs_allocate_job(hdev, queue_type, - is_kernel_allocated_cb); - if (!job) { - hpriv->ctx->cs_counters.out_of_mem_drop_cnt++; - dev_err(hdev->dev, "Failed to allocate a new job\n"); - rc = -ENOMEM; - if (is_kernel_allocated_cb) - goto release_cb; - else - goto free_cs_object; - } - - job->id = i + 1; - job->cs = cs; - job->user_cb = cb; - job->user_cb_size = chunk->cb_size; - job->hw_queue_id = chunk->queue_index; - - cs->jobs_in_queue_cnt[job->hw_queue_id]++; - - list_add_tail(&job->cs_node, &cs->job_list); - - /* - * Increment CS reference. When CS reference is 0, CS is - * done and can be signaled to user and free all its resources - * Only increment for JOB on external or H/W queues, because - * only for those JOBs we get completion - */ - if (job->queue_type == QUEUE_TYPE_EXT || - job->queue_type == QUEUE_TYPE_HW) - cs_get(cs); - - hl_debugfs_add_job(hdev, job); - - rc = cs_parser(hpriv, job); - if (rc) { - hpriv->ctx->cs_counters.parsing_drop_cnt++; - dev_err(hdev->dev, - "Failed to parse JOB %d.%llu.%d, err %d, rejecting the CS\n", - cs->ctx->asid, cs->sequence, job->id, rc); - goto free_cs_object; - } - } - - if (int_queues_only) { - hpriv->ctx->cs_counters.parsing_drop_cnt++; - dev_err(hdev->dev, - "Reject CS %d.%llu because only internal queues jobs are present\n", - cs->ctx->asid, cs->sequence); - rc = -EINVAL; - goto free_cs_object; - } - - rc = hl_hw_queue_schedule_cs(cs); - if (rc) { - if (rc != -EAGAIN) - dev_err(hdev->dev, - "Failed to submit CS %d.%llu to H/W queues, error %d\n", - cs->ctx->asid, cs->sequence, rc); - goto free_cs_object; - } - - rc = HL_CS_STATUS_SUCCESS; - goto put_cs; - -release_cb: - spin_lock(&cb->lock); - cb->cs_cnt--; - spin_unlock(&cb->lock); - hl_cb_put(cb); -free_cs_object: - cs_rollback(hdev, cs); - *cs_seq = ULLONG_MAX; - /* The path below is both for good and erroneous exits */ -put_cs: - /* We finished with the CS in this function, so put the ref */ - cs_put(cs); -free_cs_chunk_array: - kfree(cs_chunk_array); -out: - return rc; -} - -static int cs_ioctl_signal_wait(struct hl_fpriv *hpriv, enum hl_cs_type cs_type, - void __user *chunks, u32 num_chunks, - u64 *cs_seq) -{ - struct hl_device *hdev = hpriv->hdev; - struct hl_ctx *ctx = hpriv->ctx; - struct hl_cs_chunk *cs_chunk_array, *chunk; - struct hw_queue_properties *hw_queue_prop; - struct dma_fence *sig_fence = NULL; - struct hl_cs_job *job; - struct hl_cs *cs; - struct hl_cb *cb; - enum hl_queue_type q_type; - u64 *signal_seq_arr = NULL, signal_seq; - u32 size_to_copy, q_idx, signal_seq_arr_len, cb_size; - int rc; - - *cs_seq = ULLONG_MAX; - - if (num_chunks > HL_MAX_JOBS_PER_CS) { - dev_err(hdev->dev, - "Number of chunks can NOT be larger than %d\n", - HL_MAX_JOBS_PER_CS); - rc = -EINVAL; - goto out; - } - - cs_chunk_array = kmalloc_array(num_chunks, sizeof(*cs_chunk_array), - GFP_ATOMIC); - if (!cs_chunk_array) { - rc = -ENOMEM; - goto out; - } - - size_to_copy = num_chunks * sizeof(struct hl_cs_chunk); - if (copy_from_user(cs_chunk_array, chunks, size_to_copy)) { - dev_err(hdev->dev, "Failed to copy cs chunk array from user\n"); - rc = -EFAULT; - goto free_cs_chunk_array; - } - - /* currently it is guaranteed to have only one chunk */ - chunk = &cs_chunk_array[0]; - q_idx = chunk->queue_index; - hw_queue_prop = &hdev->asic_prop.hw_queues_props[q_idx]; - q_type = hw_queue_prop->type; - - if ((q_idx >= hdev->asic_prop.max_queues) || - (!hw_queue_prop->supports_sync_stream)) { - dev_err(hdev->dev, "Queue index %d is invalid\n", q_idx); - rc = -EINVAL; - goto free_cs_chunk_array; - } - - if (cs_type == CS_TYPE_WAIT) { - struct hl_cs_compl *sig_waitcs_cmpl; - - signal_seq_arr_len = chunk->num_signal_seq_arr; - - /* currently only one signal seq is supported */ - if (signal_seq_arr_len != 1) { - dev_err(hdev->dev, - "Wait for signal CS supports only one signal CS seq\n"); - rc = -EINVAL; - goto free_cs_chunk_array; - } - - signal_seq_arr = kmalloc_array(signal_seq_arr_len, - sizeof(*signal_seq_arr), - GFP_ATOMIC); - if (!signal_seq_arr) { - rc = -ENOMEM; - goto free_cs_chunk_array; - } - - size_to_copy = chunk->num_signal_seq_arr * - sizeof(*signal_seq_arr); - if (copy_from_user(signal_seq_arr, - u64_to_user_ptr(chunk->signal_seq_arr), - size_to_copy)) { - dev_err(hdev->dev, - "Failed to copy signal seq array from user\n"); - rc = -EFAULT; - goto free_signal_seq_array; - } - - /* currently it is guaranteed to have only one signal seq */ - signal_seq = signal_seq_arr[0]; - sig_fence = hl_ctx_get_fence(ctx, signal_seq); - if (IS_ERR(sig_fence)) { - dev_err(hdev->dev, - "Failed to get signal CS with seq 0x%llx\n", - signal_seq); - rc = PTR_ERR(sig_fence); - goto free_signal_seq_array; - } - - if (!sig_fence) { - /* signal CS already finished */ - rc = 0; - goto free_signal_seq_array; - } - - sig_waitcs_cmpl = - container_of(sig_fence, struct hl_cs_compl, base_fence); - - if (sig_waitcs_cmpl->type != CS_TYPE_SIGNAL) { - dev_err(hdev->dev, - "CS seq 0x%llx is not of a signal CS\n", - signal_seq); - dma_fence_put(sig_fence); - rc = -EINVAL; - goto free_signal_seq_array; - } - - if (dma_fence_is_signaled(sig_fence)) { - /* signal CS already finished */ - dma_fence_put(sig_fence); - rc = 0; - goto free_signal_seq_array; - } - } - - /* increment refcnt for context */ - hl_ctx_get(hdev, ctx); - - rc = allocate_cs(hdev, ctx, cs_type, &cs); - if (rc) { - if (cs_type == CS_TYPE_WAIT) - dma_fence_put(sig_fence); - hl_ctx_put(ctx); - goto free_signal_seq_array; - } - - /* - * Save the signal CS fence for later initialization right before - * hanging the wait CS on the queue. - */ - if (cs->type == CS_TYPE_WAIT) - cs->signal_fence = sig_fence; - - hl_debugfs_add_cs(cs); - - *cs_seq = cs->sequence; - - job = hl_cs_allocate_job(hdev, q_type, true); - if (!job) { - ctx->cs_counters.out_of_mem_drop_cnt++; - dev_err(hdev->dev, "Failed to allocate a new job\n"); - rc = -ENOMEM; - goto put_cs; - } - - cb = hl_cb_kernel_create(hdev, PAGE_SIZE); - if (!cb) { - ctx->cs_counters.out_of_mem_drop_cnt++; - kfree(job); - rc = -EFAULT; - goto put_cs; - } - - if (cs->type == CS_TYPE_WAIT) - cb_size = hdev->asic_funcs->get_wait_cb_size(hdev); - else - cb_size = hdev->asic_funcs->get_signal_cb_size(hdev); - - job->id = 0; - job->cs = cs; - job->user_cb = cb; - job->user_cb->cs_cnt++; - job->user_cb_size = cb_size; - job->hw_queue_id = q_idx; - - /* - * No need in parsing, user CB is the patched CB. - * We call hl_cb_destroy() out of two reasons - we don't need the CB in - * the CB idr anymore and to decrement its refcount as it was - * incremented inside hl_cb_kernel_create(). - */ - job->patched_cb = job->user_cb; - job->job_cb_size = job->user_cb_size; - hl_cb_destroy(hdev, &hdev->kernel_cb_mgr, cb->id << PAGE_SHIFT); - - cs->jobs_in_queue_cnt[job->hw_queue_id]++; - - list_add_tail(&job->cs_node, &cs->job_list); - - /* increment refcount as for external queues we get completion */ - cs_get(cs); - - hl_debugfs_add_job(hdev, job); - - rc = hl_hw_queue_schedule_cs(cs); - if (rc) { - if (rc != -EAGAIN) - dev_err(hdev->dev, - "Failed to submit CS %d.%llu to H/W queues, error %d\n", - ctx->asid, cs->sequence, rc); - goto free_cs_object; - } - - rc = HL_CS_STATUS_SUCCESS; - goto put_cs; - -free_cs_object: - cs_rollback(hdev, cs); - *cs_seq = ULLONG_MAX; - /* The path below is both for good and erroneous exits */ -put_cs: - /* We finished with the CS in this function, so put the ref */ - cs_put(cs); -free_signal_seq_array: - if (cs_type == CS_TYPE_WAIT) - kfree(signal_seq_arr); -free_cs_chunk_array: - kfree(cs_chunk_array); -out: - return rc; -} - -int hl_cs_ioctl(struct hl_fpriv *hpriv, void *data) -{ - struct hl_device *hdev = hpriv->hdev; - union hl_cs_args *args = data; - struct hl_ctx *ctx = hpriv->ctx; - void __user *chunks_execute, *chunks_restore; - enum hl_cs_type cs_type; - u32 num_chunks_execute, num_chunks_restore, sig_wait_flags; - u64 cs_seq = ULONG_MAX; - int rc, do_ctx_switch; - bool need_soft_reset = false; - - if (hl_device_disabled_or_in_reset(hdev)) { - dev_warn_ratelimited(hdev->dev, - "Device is %s. Can't submit new CS\n", - atomic_read(&hdev->in_reset) ? "in_reset" : "disabled"); - rc = -EBUSY; - goto out; - } - - sig_wait_flags = args->in.cs_flags & HL_CS_FLAGS_SIG_WAIT; - - if (unlikely(sig_wait_flags == HL_CS_FLAGS_SIG_WAIT)) { - dev_err(hdev->dev, - "Signal and wait CS flags are mutually exclusive, context %d\n", - ctx->asid); - rc = -EINVAL; - goto out; - } - - if (unlikely((sig_wait_flags & HL_CS_FLAGS_SIG_WAIT) && - (!hdev->supports_sync_stream))) { - dev_err(hdev->dev, "Sync stream CS is not supported\n"); - rc = -EINVAL; - goto out; - } - - if (args->in.cs_flags & HL_CS_FLAGS_SIGNAL) - cs_type = CS_TYPE_SIGNAL; - else if (args->in.cs_flags & HL_CS_FLAGS_WAIT) - cs_type = CS_TYPE_WAIT; - else - cs_type = CS_TYPE_DEFAULT; - - chunks_execute = (void __user *) (uintptr_t) args->in.chunks_execute; - num_chunks_execute = args->in.num_chunks_execute; - - if (cs_type == CS_TYPE_DEFAULT) { - if (!num_chunks_execute) { - dev_err(hdev->dev, - "Got execute CS with 0 chunks, context %d\n", - ctx->asid); - rc = -EINVAL; - goto out; - } - } else if (num_chunks_execute != 1) { - dev_err(hdev->dev, - "Sync stream CS mandates one chunk only, context %d\n", - ctx->asid); - rc = -EINVAL; - goto out; - } - - do_ctx_switch = atomic_cmpxchg(&ctx->thread_ctx_switch_token, 1, 0); - - if (do_ctx_switch || (args->in.cs_flags & HL_CS_FLAGS_FORCE_RESTORE)) { - long ret; - - chunks_restore = - (void __user *) (uintptr_t) args->in.chunks_restore; - num_chunks_restore = args->in.num_chunks_restore; - - mutex_lock(&hpriv->restore_phase_mutex); - - if (do_ctx_switch) { - rc = hdev->asic_funcs->context_switch(hdev, ctx->asid); - if (rc) { - dev_err_ratelimited(hdev->dev, - "Failed to switch to context %d, rejecting CS! %d\n", - ctx->asid, rc); - /* - * If we timedout, or if the device is not IDLE - * while we want to do context-switch (-EBUSY), - * we need to soft-reset because QMAN is - * probably stuck. However, we can't call to - * reset here directly because of deadlock, so - * need to do it at the very end of this - * function - */ - if ((rc == -ETIMEDOUT) || (rc == -EBUSY)) - need_soft_reset = true; - mutex_unlock(&hpriv->restore_phase_mutex); - goto out; - } - } - - hdev->asic_funcs->restore_phase_topology(hdev); - - if (!num_chunks_restore) { - dev_dbg(hdev->dev, - "Need to run restore phase but restore CS is empty\n"); - rc = 0; - } else { - rc = cs_ioctl_default(hpriv, chunks_restore, - num_chunks_restore, &cs_seq); - } - - mutex_unlock(&hpriv->restore_phase_mutex); - - if (rc) { - dev_err(hdev->dev, - "Failed to submit restore CS for context %d (%d)\n", - ctx->asid, rc); - goto out; - } - - /* Need to wait for restore completion before execution phase */ - if (num_chunks_restore) { - ret = _hl_cs_wait_ioctl(hdev, ctx, - jiffies_to_usecs(hdev->timeout_jiffies), - cs_seq); - if (ret <= 0) { - dev_err(hdev->dev, - "Restore CS for context %d failed to complete %ld\n", - ctx->asid, ret); - rc = -ENOEXEC; - goto out; - } - } - - ctx->thread_ctx_switch_wait_token = 1; - } else if (!ctx->thread_ctx_switch_wait_token) { - u32 tmp; - - rc = hl_poll_timeout_memory(hdev, - &ctx->thread_ctx_switch_wait_token, tmp, (tmp == 1), - 100, jiffies_to_usecs(hdev->timeout_jiffies), false); - - if (rc == -ETIMEDOUT) { - dev_err(hdev->dev, - "context switch phase timeout (%d)\n", tmp); - goto out; - } - } - - if (cs_type == CS_TYPE_DEFAULT) - rc = cs_ioctl_default(hpriv, chunks_execute, num_chunks_execute, - &cs_seq); - else - rc = cs_ioctl_signal_wait(hpriv, cs_type, chunks_execute, - num_chunks_execute, &cs_seq); - -out: - if (rc != -EAGAIN) { - memset(args, 0, sizeof(*args)); - args->out.status = rc; - args->out.seq = cs_seq; - } - - if (((rc == -ETIMEDOUT) || (rc == -EBUSY)) && (need_soft_reset)) - hl_device_reset(hdev, false, false); - - return rc; -} - -static long _hl_cs_wait_ioctl(struct hl_device *hdev, - struct hl_ctx *ctx, u64 timeout_us, u64 seq) -{ - struct dma_fence *fence; - unsigned long timeout; - long rc; - - if (timeout_us == MAX_SCHEDULE_TIMEOUT) - timeout = timeout_us; - else - timeout = usecs_to_jiffies(timeout_us); - - hl_ctx_get(hdev, ctx); - - fence = hl_ctx_get_fence(ctx, seq); - if (IS_ERR(fence)) { - rc = PTR_ERR(fence); - if (rc == -EINVAL) - dev_notice_ratelimited(hdev->dev, - "Can't wait on CS %llu because current CS is at seq %llu\n", - seq, ctx->cs_sequence); - } else if (fence) { - rc = dma_fence_wait_timeout(fence, true, timeout); - if (fence->error == -ETIMEDOUT) - rc = -ETIMEDOUT; - else if (fence->error == -EIO) - rc = -EIO; - dma_fence_put(fence); - } else { - dev_dbg(hdev->dev, - "Can't wait on seq %llu because current CS is at seq %llu (Fence is gone)\n", - seq, ctx->cs_sequence); - rc = 1; - } - - hl_ctx_put(ctx); - - return rc; -} - -int hl_cs_wait_ioctl(struct hl_fpriv *hpriv, void *data) -{ - struct hl_device *hdev = hpriv->hdev; - union hl_wait_cs_args *args = data; - u64 seq = args->in.seq; - long rc; - - rc = _hl_cs_wait_ioctl(hdev, hpriv->ctx, args->in.timeout_us, seq); - - memset(args, 0, sizeof(*args)); - - if (rc < 0) { - if (rc == -ERESTARTSYS) { - dev_err_ratelimited(hdev->dev, - "user process got signal while waiting for CS handle %llu\n", - seq); - args->out.status = HL_WAIT_CS_STATUS_INTERRUPTED; - rc = -EINTR; - } else if (rc == -ETIMEDOUT) { - dev_err_ratelimited(hdev->dev, - "CS %llu has timed-out while user process is waiting for it\n", - seq); - args->out.status = HL_WAIT_CS_STATUS_TIMEDOUT; - } else if (rc == -EIO) { - dev_err_ratelimited(hdev->dev, - "CS %llu has been aborted while user process is waiting for it\n", - seq); - args->out.status = HL_WAIT_CS_STATUS_ABORTED; - } - return rc; - } - - if (rc == 0) - args->out.status = HL_WAIT_CS_STATUS_BUSY; - else - args->out.status = HL_WAIT_CS_STATUS_COMPLETED; - - return 0; -} diff --git a/drivers/misc/habanalabs/common/Makefile b/drivers/misc/habanalabs/common/Makefile new file mode 100644 index 000000000000..97d03b5c8683 --- /dev/null +++ b/drivers/misc/habanalabs/common/Makefile @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0-only +subdir-ccflags-y += -I$(src)/common + +HL_COMMON_FILES := common/habanalabs_drv.o common/device.o common/context.o \ + common/asid.o common/habanalabs_ioctl.o \ + common/command_buffer.o common/hw_queue.o common/irq.o \ + common/sysfs.o common/hwmon.o common/memory.o \ + common/command_submission.o common/mmu.o common/firmware_if.o \ + common/pci.o diff --git a/drivers/misc/habanalabs/common/asid.c b/drivers/misc/habanalabs/common/asid.c new file mode 100644 index 000000000000..a2fdf31cf27c --- /dev/null +++ b/drivers/misc/habanalabs/common/asid.c @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright 2016-2019 HabanaLabs, Ltd. + * All Rights Reserved. + */ + +#include "habanalabs.h" + +#include + +int hl_asid_init(struct hl_device *hdev) +{ + hdev->asid_bitmap = kcalloc(BITS_TO_LONGS(hdev->asic_prop.max_asid), + sizeof(*hdev->asid_bitmap), GFP_KERNEL); + if (!hdev->asid_bitmap) + return -ENOMEM; + + mutex_init(&hdev->asid_mutex); + + /* ASID 0 is reserved for the kernel driver and device CPU */ + set_bit(0, hdev->asid_bitmap); + + return 0; +} + +void hl_asid_fini(struct hl_device *hdev) +{ + mutex_destroy(&hdev->asid_mutex); + kfree(hdev->asid_bitmap); +} + +unsigned long hl_asid_alloc(struct hl_device *hdev) +{ + unsigned long found; + + mutex_lock(&hdev->asid_mutex); + + found = find_first_zero_bit(hdev->asid_bitmap, + hdev->asic_prop.max_asid); + if (found == hdev->asic_prop.max_asid) + found = 0; + else + set_bit(found, hdev->asid_bitmap); + + mutex_unlock(&hdev->asid_mutex); + + return found; +} + +void hl_asid_free(struct hl_device *hdev, unsigned long asid) +{ + if (WARN((asid == 0 || asid >= hdev->asic_prop.max_asid), + "Invalid ASID %lu", asid)) + return; + clear_bit(asid, hdev->asid_bitmap); +} diff --git a/drivers/misc/habanalabs/common/command_buffer.c b/drivers/misc/habanalabs/common/command_buffer.c new file mode 100644 index 000000000000..02d13f71b1df --- /dev/null +++ b/drivers/misc/habanalabs/common/command_buffer.c @@ -0,0 +1,463 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright 2016-2019 HabanaLabs, Ltd. + * All Rights Reserved. + */ + +#include +#include "habanalabs.h" + +#include +#include + +static void cb_fini(struct hl_device *hdev, struct hl_cb *cb) +{ + hdev->asic_funcs->asic_dma_free_coherent(hdev, cb->size, + (void *) (uintptr_t) cb->kernel_address, + cb->bus_address); + kfree(cb); +} + +static void cb_do_release(struct hl_device *hdev, struct hl_cb *cb) +{ + if (cb->is_pool) { + spin_lock(&hdev->cb_pool_lock); + list_add(&cb->pool_list, &hdev->cb_pool); + spin_unlock(&hdev->cb_pool_lock); + } else { + cb_fini(hdev, cb); + } +} + +static void cb_release(struct kref *ref) +{ + struct hl_device *hdev; + struct hl_cb *cb; + + cb = container_of(ref, struct hl_cb, refcount); + hdev = cb->hdev; + + hl_debugfs_remove_cb(cb); + + cb_do_release(hdev, cb); +} + +static struct hl_cb *hl_cb_alloc(struct hl_device *hdev, u32 cb_size, + int ctx_id) +{ + struct hl_cb *cb; + void *p; + + /* + * We use of GFP_ATOMIC here because this function can be called from + * the latency-sensitive code path for command submission. Due to H/W + * limitations in some of the ASICs, the kernel must copy the user CB + * that is designated for an external queue and actually enqueue + * the kernel's copy. Hence, we must never sleep in this code section + * and must use GFP_ATOMIC for all memory allocations. + */ + if (ctx_id == HL_KERNEL_ASID_ID) + cb = kzalloc(sizeof(*cb), GFP_ATOMIC); + else + cb = kzalloc(sizeof(*cb), GFP_KERNEL); + + if (!cb) + return NULL; + + if (ctx_id == HL_KERNEL_ASID_ID) + p = hdev->asic_funcs->asic_dma_alloc_coherent(hdev, cb_size, + &cb->bus_address, GFP_ATOMIC); + else + p = hdev->asic_funcs->asic_dma_alloc_coherent(hdev, cb_size, + &cb->bus_address, + GFP_USER | __GFP_ZERO); + if (!p) { + dev_err(hdev->dev, + "failed to allocate %d of dma memory for CB\n", + cb_size); + kfree(cb); + return NULL; + } + + cb->kernel_address = (u64) (uintptr_t) p; + cb->size = cb_size; + + return cb; +} + +int hl_cb_create(struct hl_device *hdev, struct hl_cb_mgr *mgr, + u32 cb_size, u64 *handle, int ctx_id) +{ + struct hl_cb *cb; + bool alloc_new_cb = true; + int rc; + + /* + * Can't use generic function to check this because of special case + * where we create a CB as part of the reset process + */ + if ((hdev->disabled) || ((atomic_read(&hdev->in_reset)) && + (ctx_id != HL_KERNEL_ASID_ID))) { + dev_warn_ratelimited(hdev->dev, + "Device is disabled or in reset. Can't create new CBs\n"); + rc = -EBUSY; + goto out_err; + } + + if (cb_size > SZ_2M) { + dev_err(hdev->dev, "CB size %d must be less than %d\n", + cb_size, SZ_2M); + rc = -EINVAL; + goto out_err; + } + + /* Minimum allocation must be PAGE SIZE */ + if (cb_size < PAGE_SIZE) + cb_size = PAGE_SIZE; + + if (ctx_id == HL_KERNEL_ASID_ID && + cb_size <= hdev->asic_prop.cb_pool_cb_size) { + + spin_lock(&hdev->cb_pool_lock); + if (!list_empty(&hdev->cb_pool)) { + cb = list_first_entry(&hdev->cb_pool, typeof(*cb), + pool_list); + list_del(&cb->pool_list); + spin_unlock(&hdev->cb_pool_lock); + alloc_new_cb = false; + } else { + spin_unlock(&hdev->cb_pool_lock); + dev_dbg(hdev->dev, "CB pool is empty\n"); + } + } + + if (alloc_new_cb) { + cb = hl_cb_alloc(hdev, cb_size, ctx_id); + if (!cb) { + rc = -ENOMEM; + goto out_err; + } + } + + cb->hdev = hdev; + cb->ctx_id = ctx_id; + + spin_lock(&mgr->cb_lock); + rc = idr_alloc(&mgr->cb_handles, cb, 1, 0, GFP_ATOMIC); + spin_unlock(&mgr->cb_lock); + + if (rc < 0) { + dev_err(hdev->dev, "Failed to allocate IDR for a new CB\n"); + goto release_cb; + } + + cb->id = rc; + + kref_init(&cb->refcount); + spin_lock_init(&cb->lock); + + /* + * idr is 32-bit so we can safely OR it with a mask that is above + * 32 bit + */ + *handle = cb->id | HL_MMAP_CB_MASK; + *handle <<= PAGE_SHIFT; + + hl_debugfs_add_cb(cb); + + return 0; + +release_cb: + cb_do_release(hdev, cb); +out_err: + *handle = 0; + + return rc; +} + +int hl_cb_destroy(struct hl_device *hdev, struct hl_cb_mgr *mgr, u64 cb_handle) +{ + struct hl_cb *cb; + u32 handle; + int rc = 0; + + /* + * handle was given to user to do mmap, I need to shift it back to + * how the idr module gave it to me + */ + cb_handle >>= PAGE_SHIFT; + handle = (u32) cb_handle; + + spin_lock(&mgr->cb_lock); + + cb = idr_find(&mgr->cb_handles, handle); + if (cb) { + idr_remove(&mgr->cb_handles, handle); + spin_unlock(&mgr->cb_lock); + kref_put(&cb->refcount, cb_release); + } else { + spin_unlock(&mgr->cb_lock); + dev_err(hdev->dev, + "CB destroy failed, no match to handle 0x%x\n", handle); + rc = -EINVAL; + } + + return rc; +} + +int hl_cb_ioctl(struct hl_fpriv *hpriv, void *data) +{ + union hl_cb_args *args = data; + struct hl_device *hdev = hpriv->hdev; + u64 handle = 0; + int rc; + + if (hl_device_disabled_or_in_reset(hdev)) { + dev_warn_ratelimited(hdev->dev, + "Device is %s. Can't execute CB IOCTL\n", + atomic_read(&hdev->in_reset) ? "in_reset" : "disabled"); + return -EBUSY; + } + + switch (args->in.op) { + case HL_CB_OP_CREATE: + if (args->in.cb_size > HL_MAX_CB_SIZE) { + dev_err(hdev->dev, + "User requested CB size %d must be less than %d\n", + args->in.cb_size, HL_MAX_CB_SIZE); + rc = -EINVAL; + } else { + rc = hl_cb_create(hdev, &hpriv->cb_mgr, + args->in.cb_size, &handle, + hpriv->ctx->asid); + } + + memset(args, 0, sizeof(*args)); + args->out.cb_handle = handle; + break; + + case HL_CB_OP_DESTROY: + rc = hl_cb_destroy(hdev, &hpriv->cb_mgr, + args->in.cb_handle); + break; + + default: + rc = -ENOTTY; + break; + } + + return rc; +} + +static void cb_vm_close(struct vm_area_struct *vma) +{ + struct hl_cb *cb = (struct hl_cb *) vma->vm_private_data; + long new_mmap_size; + + new_mmap_size = cb->mmap_size - (vma->vm_end - vma->vm_start); + + if (new_mmap_size > 0) { + cb->mmap_size = new_mmap_size; + return; + } + + spin_lock(&cb->lock); + cb->mmap = false; + spin_unlock(&cb->lock); + + hl_cb_put(cb); + vma->vm_private_data = NULL; +} + +static const struct vm_operations_struct cb_vm_ops = { + .close = cb_vm_close +}; + +int hl_cb_mmap(struct hl_fpriv *hpriv, struct vm_area_struct *vma) +{ + struct hl_device *hdev = hpriv->hdev; + struct hl_cb *cb; + phys_addr_t address; + u32 handle; + int rc; + + handle = vma->vm_pgoff; + + /* reference was taken here */ + cb = hl_cb_get(hdev, &hpriv->cb_mgr, handle); + if (!cb) { + dev_err(hdev->dev, + "CB mmap failed, no match to handle 0x%x\n", handle); + return -EINVAL; + } + + /* Validation check */ + if ((vma->vm_end - vma->vm_start) != ALIGN(cb->size, PAGE_SIZE)) { + dev_err(hdev->dev, + "CB mmap failed, mmap size 0x%lx != 0x%x cb size\n", + vma->vm_end - vma->vm_start, cb->size); + rc = -EINVAL; + goto put_cb; + } + + spin_lock(&cb->lock); + + if (cb->mmap) { + dev_err(hdev->dev, + "CB mmap failed, CB already mmaped to user\n"); + rc = -EINVAL; + goto release_lock; + } + + cb->mmap = true; + + spin_unlock(&cb->lock); + + vma->vm_ops = &cb_vm_ops; + + /* + * Note: We're transferring the cb reference to + * vma->vm_private_data here. + */ + + vma->vm_private_data = cb; + + /* Calculate address for CB */ + address = virt_to_phys((void *) (uintptr_t) cb->kernel_address); + + rc = hdev->asic_funcs->cb_mmap(hdev, vma, cb->kernel_address, + address, cb->size); + + if (rc) { + spin_lock(&cb->lock); + cb->mmap = false; + goto release_lock; + } + + cb->mmap_size = cb->size; + + return 0; + +release_lock: + spin_unlock(&cb->lock); +put_cb: + hl_cb_put(cb); + return rc; +} + +struct hl_cb *hl_cb_get(struct hl_device *hdev, struct hl_cb_mgr *mgr, + u32 handle) +{ + struct hl_cb *cb; + + spin_lock(&mgr->cb_lock); + cb = idr_find(&mgr->cb_handles, handle); + + if (!cb) { + spin_unlock(&mgr->cb_lock); + dev_warn(hdev->dev, + "CB get failed, no match to handle 0x%x\n", handle); + return NULL; + } + + kref_get(&cb->refcount); + + spin_unlock(&mgr->cb_lock); + + return cb; + +} + +void hl_cb_put(struct hl_cb *cb) +{ + kref_put(&cb->refcount, cb_release); +} + +void hl_cb_mgr_init(struct hl_cb_mgr *mgr) +{ + spin_lock_init(&mgr->cb_lock); + idr_init(&mgr->cb_handles); +} + +void hl_cb_mgr_fini(struct hl_device *hdev, struct hl_cb_mgr *mgr) +{ + struct hl_cb *cb; + struct idr *idp; + u32 id; + + idp = &mgr->cb_handles; + + idr_for_each_entry(idp, cb, id) { + if (kref_put(&cb->refcount, cb_release) != 1) + dev_err(hdev->dev, + "CB %d for CTX ID %d is still alive\n", + id, cb->ctx_id); + } + + idr_destroy(&mgr->cb_handles); +} + +struct hl_cb *hl_cb_kernel_create(struct hl_device *hdev, u32 cb_size) +{ + u64 cb_handle; + struct hl_cb *cb; + int rc; + + rc = hl_cb_create(hdev, &hdev->kernel_cb_mgr, cb_size, &cb_handle, + HL_KERNEL_ASID_ID); + if (rc) { + dev_err(hdev->dev, + "Failed to allocate CB for the kernel driver %d\n", rc); + return NULL; + } + + cb_handle >>= PAGE_SHIFT; + cb = hl_cb_get(hdev, &hdev->kernel_cb_mgr, (u32) cb_handle); + /* hl_cb_get should never fail here so use kernel WARN */ + WARN(!cb, "Kernel CB handle invalid 0x%x\n", (u32) cb_handle); + if (!cb) + goto destroy_cb; + + return cb; + +destroy_cb: + hl_cb_destroy(hdev, &hdev->kernel_cb_mgr, cb_handle << PAGE_SHIFT); + + return NULL; +} + +int hl_cb_pool_init(struct hl_device *hdev) +{ + struct hl_cb *cb; + int i; + + INIT_LIST_HEAD(&hdev->cb_pool); + spin_lock_init(&hdev->cb_pool_lock); + + for (i = 0 ; i < hdev->asic_prop.cb_pool_cb_cnt ; i++) { + cb = hl_cb_alloc(hdev, hdev->asic_prop.cb_pool_cb_size, + HL_KERNEL_ASID_ID); + if (cb) { + cb->is_pool = true; + list_add(&cb->pool_list, &hdev->cb_pool); + } else { + hl_cb_pool_fini(hdev); + return -ENOMEM; + } + } + + return 0; +} + +int hl_cb_pool_fini(struct hl_device *hdev) +{ + struct hl_cb *cb, *tmp; + + list_for_each_entry_safe(cb, tmp, &hdev->cb_pool, pool_list) { + list_del(&cb->pool_list); + cb_fini(hdev, cb); + } + + return 0; +} diff --git a/drivers/misc/habanalabs/common/command_submission.c b/drivers/misc/habanalabs/common/command_submission.c new file mode 100644 index 000000000000..c605be89f764 --- /dev/null +++ b/drivers/misc/habanalabs/common/command_submission.c @@ -0,0 +1,1232 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright 2016-2019 HabanaLabs, Ltd. + * All Rights Reserved. + */ + +#include +#include "habanalabs.h" + +#include +#include + +#define HL_CS_FLAGS_SIG_WAIT (HL_CS_FLAGS_SIGNAL | HL_CS_FLAGS_WAIT) + +static void job_wq_completion(struct work_struct *work); +static long _hl_cs_wait_ioctl(struct hl_device *hdev, + struct hl_ctx *ctx, u64 timeout_us, u64 seq); +static void cs_do_release(struct kref *ref); + +static void hl_sob_reset(struct kref *ref) +{ + struct hl_hw_sob *hw_sob = container_of(ref, struct hl_hw_sob, + kref); + struct hl_device *hdev = hw_sob->hdev; + + hdev->asic_funcs->reset_sob(hdev, hw_sob); +} + +void hl_sob_reset_error(struct kref *ref) +{ + struct hl_hw_sob *hw_sob = container_of(ref, struct hl_hw_sob, + kref); + struct hl_device *hdev = hw_sob->hdev; + + dev_crit(hdev->dev, + "SOB release shouldn't be called here, q_idx: %d, sob_id: %d\n", + hw_sob->q_idx, hw_sob->sob_id); +} + +static const char *hl_fence_get_driver_name(struct dma_fence *fence) +{ + return "HabanaLabs"; +} + +static const char *hl_fence_get_timeline_name(struct dma_fence *fence) +{ + struct hl_cs_compl *hl_cs_compl = + container_of(fence, struct hl_cs_compl, base_fence); + + return dev_name(hl_cs_compl->hdev->dev); +} + +static bool hl_fence_enable_signaling(struct dma_fence *fence) +{ + return true; +} + +static void hl_fence_release(struct dma_fence *fence) +{ + struct hl_cs_compl *hl_cs_cmpl = + container_of(fence, struct hl_cs_compl, base_fence); + struct hl_device *hdev = hl_cs_cmpl->hdev; + + /* EBUSY means the CS was never submitted and hence we don't have + * an attached hw_sob object that we should handle here + */ + if (fence->error == -EBUSY) + goto free; + + if ((hl_cs_cmpl->type == CS_TYPE_SIGNAL) || + (hl_cs_cmpl->type == CS_TYPE_WAIT)) { + + dev_dbg(hdev->dev, + "CS 0x%llx type %d finished, sob_id: %d, sob_val: 0x%x\n", + hl_cs_cmpl->cs_seq, + hl_cs_cmpl->type, + hl_cs_cmpl->hw_sob->sob_id, + hl_cs_cmpl->sob_val); + + /* + * A signal CS can get completion while the corresponding wait + * for signal CS is on its way to the PQ. The wait for signal CS + * will get stuck if the signal CS incremented the SOB to its + * max value and there are no pending (submitted) waits on this + * SOB. + * We do the following to void this situation: + * 1. The wait for signal CS must get a ref for the signal CS as + * soon as possible in cs_ioctl_signal_wait() and put it + * before being submitted to the PQ but after it incremented + * the SOB refcnt in init_signal_wait_cs(). + * 2. Signal/Wait for signal CS will decrement the SOB refcnt + * here. + * These two measures guarantee that the wait for signal CS will + * reset the SOB upon completion rather than the signal CS and + * hence the above scenario is avoided. + */ + kref_put(&hl_cs_cmpl->hw_sob->kref, hl_sob_reset); + } + +free: + kfree_rcu(hl_cs_cmpl, base_fence.rcu); +} + +static const struct dma_fence_ops hl_fence_ops = { + .get_driver_name = hl_fence_get_driver_name, + .get_timeline_name = hl_fence_get_timeline_name, + .enable_signaling = hl_fence_enable_signaling, + .release = hl_fence_release +}; + +static void cs_get(struct hl_cs *cs) +{ + kref_get(&cs->refcount); +} + +static int cs_get_unless_zero(struct hl_cs *cs) +{ + return kref_get_unless_zero(&cs->refcount); +} + +static void cs_put(struct hl_cs *cs) +{ + kref_put(&cs->refcount, cs_do_release); +} + +static bool is_cb_patched(struct hl_device *hdev, struct hl_cs_job *job) +{ + /* + * Patched CB is created for external queues jobs, and for H/W queues + * jobs if the user CB was allocated by driver and MMU is disabled. + */ + return (job->queue_type == QUEUE_TYPE_EXT || + (job->queue_type == QUEUE_TYPE_HW && + job->is_kernel_allocated_cb && + !hdev->mmu_enable)); +} + +/* + * cs_parser - parse the user command submission + * + * @hpriv : pointer to the private data of the fd + * @job : pointer to the job that holds the command submission info + * + * The function parses the command submission of the user. It calls the + * ASIC specific parser, which returns a list of memory blocks to send + * to the device as different command buffers + * + */ +static int cs_parser(struct hl_fpriv *hpriv, struct hl_cs_job *job) +{ + struct hl_device *hdev = hpriv->hdev; + struct hl_cs_parser parser; + int rc; + + parser.ctx_id = job->cs->ctx->asid; + parser.cs_sequence = job->cs->sequence; + parser.job_id = job->id; + + parser.hw_queue_id = job->hw_queue_id; + parser.job_userptr_list = &job->userptr_list; + parser.patched_cb = NULL; + parser.user_cb = job->user_cb; + parser.user_cb_size = job->user_cb_size; + parser.queue_type = job->queue_type; + parser.is_kernel_allocated_cb = job->is_kernel_allocated_cb; + job->patched_cb = NULL; + + rc = hdev->asic_funcs->cs_parser(hdev, &parser); + + if (is_cb_patched(hdev, job)) { + if (!rc) { + job->patched_cb = parser.patched_cb; + job->job_cb_size = parser.patched_cb_size; + job->contains_dma_pkt = parser.contains_dma_pkt; + + spin_lock(&job->patched_cb->lock); + job->patched_cb->cs_cnt++; + spin_unlock(&job->patched_cb->lock); + } + + /* + * Whether the parsing worked or not, we don't need the + * original CB anymore because it was already parsed and + * won't be accessed again for this CS + */ + spin_lock(&job->user_cb->lock); + job->user_cb->cs_cnt--; + spin_unlock(&job->user_cb->lock); + hl_cb_put(job->user_cb); + job->user_cb = NULL; + } else if (!rc) { + job->job_cb_size = job->user_cb_size; + } + + return rc; +} + +static void free_job(struct hl_device *hdev, struct hl_cs_job *job) +{ + struct hl_cs *cs = job->cs; + + if (is_cb_patched(hdev, job)) { + hl_userptr_delete_list(hdev, &job->userptr_list); + + /* + * We might arrive here from rollback and patched CB wasn't + * created, so we need to check it's not NULL + */ + if (job->patched_cb) { + spin_lock(&job->patched_cb->lock); + job->patched_cb->cs_cnt--; + spin_unlock(&job->patched_cb->lock); + + hl_cb_put(job->patched_cb); + } + } + + /* For H/W queue jobs, if a user CB was allocated by driver and MMU is + * enabled, the user CB isn't released in cs_parser() and thus should be + * released here. + */ + if (job->queue_type == QUEUE_TYPE_HW && + job->is_kernel_allocated_cb && hdev->mmu_enable) { + spin_lock(&job->user_cb->lock); + job->user_cb->cs_cnt--; + spin_unlock(&job->user_cb->lock); + + hl_cb_put(job->user_cb); + } + + /* + * This is the only place where there can be multiple threads + * modifying the list at the same time + */ + spin_lock(&cs->job_lock); + list_del(&job->cs_node); + spin_unlock(&cs->job_lock); + + hl_debugfs_remove_job(hdev, job); + + if (job->queue_type == QUEUE_TYPE_EXT || + job->queue_type == QUEUE_TYPE_HW) + cs_put(cs); + + kfree(job); +} + +static void cs_counters_aggregate(struct hl_device *hdev, struct hl_ctx *ctx) +{ + hdev->aggregated_cs_counters.device_in_reset_drop_cnt += + ctx->cs_counters.device_in_reset_drop_cnt; + hdev->aggregated_cs_counters.out_of_mem_drop_cnt += + ctx->cs_counters.out_of_mem_drop_cnt; + hdev->aggregated_cs_counters.parsing_drop_cnt += + ctx->cs_counters.parsing_drop_cnt; + hdev->aggregated_cs_counters.queue_full_drop_cnt += + ctx->cs_counters.queue_full_drop_cnt; +} + +static void cs_do_release(struct kref *ref) +{ + struct hl_cs *cs = container_of(ref, struct hl_cs, + refcount); + struct hl_device *hdev = cs->ctx->hdev; + struct hl_cs_job *job, *tmp; + + cs->completed = true; + + /* + * Although if we reached here it means that all external jobs have + * finished, because each one of them took refcnt to CS, we still + * need to go over the internal jobs and free them. Otherwise, we + * will have leaked memory and what's worse, the CS object (and + * potentially the CTX object) could be released, while the JOB + * still holds a pointer to them (but no reference). + */ + list_for_each_entry_safe(job, tmp, &cs->job_list, cs_node) + free_job(hdev, job); + + /* We also need to update CI for internal queues */ + if (cs->submitted) { + hdev->asic_funcs->hw_queues_lock(hdev); + + hdev->cs_active_cnt--; + if (!hdev->cs_active_cnt) { + struct hl_device_idle_busy_ts *ts; + + ts = &hdev->idle_busy_ts_arr[hdev->idle_busy_ts_idx++]; + ts->busy_to_idle_ts = ktime_get(); + + if (hdev->idle_busy_ts_idx == HL_IDLE_BUSY_TS_ARR_SIZE) + hdev->idle_busy_ts_idx = 0; + } else if (hdev->cs_active_cnt < 0) { + dev_crit(hdev->dev, "CS active cnt %d is negative\n", + hdev->cs_active_cnt); + } + + hdev->asic_funcs->hw_queues_unlock(hdev); + + hl_int_hw_queue_update_ci(cs); + + spin_lock(&hdev->hw_queues_mirror_lock); + /* remove CS from hw_queues mirror list */ + list_del_init(&cs->mirror_node); + spin_unlock(&hdev->hw_queues_mirror_lock); + + /* + * Don't cancel TDR in case this CS was timedout because we + * might be running from the TDR context + */ + if ((!cs->timedout) && + (hdev->timeout_jiffies != MAX_SCHEDULE_TIMEOUT)) { + struct hl_cs *next; + + if (cs->tdr_active) + cancel_delayed_work_sync(&cs->work_tdr); + + spin_lock(&hdev->hw_queues_mirror_lock); + + /* queue TDR for next CS */ + next = list_first_entry_or_null( + &hdev->hw_queues_mirror_list, + struct hl_cs, mirror_node); + + if ((next) && (!next->tdr_active)) { + next->tdr_active = true; + schedule_delayed_work(&next->work_tdr, + hdev->timeout_jiffies); + } + + spin_unlock(&hdev->hw_queues_mirror_lock); + } + } else if (cs->type == CS_TYPE_WAIT) { + /* + * In case the wait for signal CS was submitted, the put occurs + * in init_signal_wait_cs() right before hanging on the PQ. + */ + dma_fence_put(cs->signal_fence); + } + + /* + * Must be called before hl_ctx_put because inside we use ctx to get + * the device + */ + hl_debugfs_remove_cs(cs); + + hl_ctx_put(cs->ctx); + + /* We need to mark an error for not submitted because in that case + * the dma fence release flow is different. Mainly, we don't need + * to handle hw_sob for signal/wait + */ + if (cs->timedout) + dma_fence_set_error(cs->fence, -ETIMEDOUT); + else if (cs->aborted) + dma_fence_set_error(cs->fence, -EIO); + else if (!cs->submitted) + dma_fence_set_error(cs->fence, -EBUSY); + + dma_fence_signal(cs->fence); + dma_fence_put(cs->fence); + + cs_counters_aggregate(hdev, cs->ctx); + + kfree(cs->jobs_in_queue_cnt); + kfree(cs); +} + +static void cs_timedout(struct work_struct *work) +{ + struct hl_device *hdev; + int ctx_asid, rc; + struct hl_cs *cs = container_of(work, struct hl_cs, + work_tdr.work); + rc = cs_get_unless_zero(cs); + if (!rc) + return; + + if ((!cs->submitted) || (cs->completed)) { + cs_put(cs); + return; + } + + /* Mark the CS is timed out so we won't try to cancel its TDR */ + cs->timedout = true; + + hdev = cs->ctx->hdev; + ctx_asid = cs->ctx->asid; + + dev_err(hdev->dev, + "Command submission %llu has not finished in time!\n", + cs->sequence); + + cs_put(cs); + + if (hdev->reset_on_lockup) + hl_device_reset(hdev, false, false); +} + +static int allocate_cs(struct hl_device *hdev, struct hl_ctx *ctx, + enum hl_cs_type cs_type, struct hl_cs **cs_new) +{ + struct hl_cs_compl *cs_cmpl; + struct dma_fence *other = NULL; + struct hl_cs *cs; + int rc; + + cs = kzalloc(sizeof(*cs), GFP_ATOMIC); + if (!cs) + return -ENOMEM; + + cs->ctx = ctx; + cs->submitted = false; + cs->completed = false; + cs->type = cs_type; + INIT_LIST_HEAD(&cs->job_list); + INIT_DELAYED_WORK(&cs->work_tdr, cs_timedout); + kref_init(&cs->refcount); + spin_lock_init(&cs->job_lock); + + cs_cmpl = kmalloc(sizeof(*cs_cmpl), GFP_ATOMIC); + if (!cs_cmpl) { + rc = -ENOMEM; + goto free_cs; + } + + cs_cmpl->hdev = hdev; + cs_cmpl->type = cs->type; + spin_lock_init(&cs_cmpl->lock); + cs->fence = &cs_cmpl->base_fence; + + spin_lock(&ctx->cs_lock); + + cs_cmpl->cs_seq = ctx->cs_sequence; + other = ctx->cs_pending[cs_cmpl->cs_seq & + (hdev->asic_prop.max_pending_cs - 1)]; + if ((other) && (!dma_fence_is_signaled(other))) { + dev_dbg(hdev->dev, + "Rejecting CS because of too many in-flights CS\n"); + rc = -EAGAIN; + goto free_fence; + } + + cs->jobs_in_queue_cnt = kcalloc(hdev->asic_prop.max_queues, + sizeof(*cs->jobs_in_queue_cnt), GFP_ATOMIC); + if (!cs->jobs_in_queue_cnt) { + rc = -ENOMEM; + goto free_fence; + } + + dma_fence_init(&cs_cmpl->base_fence, &hl_fence_ops, &cs_cmpl->lock, + ctx->asid, ctx->cs_sequence); + + cs->sequence = cs_cmpl->cs_seq; + + ctx->cs_pending[cs_cmpl->cs_seq & + (hdev->asic_prop.max_pending_cs - 1)] = + &cs_cmpl->base_fence; + ctx->cs_sequence++; + + dma_fence_get(&cs_cmpl->base_fence); + + dma_fence_put(other); + + spin_unlock(&ctx->cs_lock); + + *cs_new = cs; + + return 0; + +free_fence: + spin_unlock(&ctx->cs_lock); + kfree(cs_cmpl); +free_cs: + kfree(cs); + return rc; +} + +static void cs_rollback(struct hl_device *hdev, struct hl_cs *cs) +{ + struct hl_cs_job *job, *tmp; + + list_for_each_entry_safe(job, tmp, &cs->job_list, cs_node) + free_job(hdev, job); +} + +void hl_cs_rollback_all(struct hl_device *hdev) +{ + int i; + struct hl_cs *cs, *tmp; + + /* flush all completions */ + for (i = 0 ; i < hdev->asic_prop.completion_queues_count ; i++) + flush_workqueue(hdev->cq_wq[i]); + + /* Make sure we don't have leftovers in the H/W queues mirror list */ + list_for_each_entry_safe(cs, tmp, &hdev->hw_queues_mirror_list, + mirror_node) { + cs_get(cs); + cs->aborted = true; + dev_warn_ratelimited(hdev->dev, "Killing CS %d.%llu\n", + cs->ctx->asid, cs->sequence); + cs_rollback(hdev, cs); + cs_put(cs); + } +} + +static void job_wq_completion(struct work_struct *work) +{ + struct hl_cs_job *job = container_of(work, struct hl_cs_job, + finish_work); + struct hl_cs *cs = job->cs; + struct hl_device *hdev = cs->ctx->hdev; + + /* job is no longer needed */ + free_job(hdev, job); +} + +static int validate_queue_index(struct hl_device *hdev, + struct hl_cs_chunk *chunk, + enum hl_queue_type *queue_type, + bool *is_kernel_allocated_cb) +{ + struct asic_fixed_properties *asic = &hdev->asic_prop; + struct hw_queue_properties *hw_queue_prop; + + /* This must be checked here to prevent out-of-bounds access to + * hw_queues_props array + */ + if (chunk->queue_index >= asic->max_queues) { + dev_err(hdev->dev, "Queue index %d is invalid\n", + chunk->queue_index); + return -EINVAL; + } + + hw_queue_prop = &asic->hw_queues_props[chunk->queue_index]; + + if (hw_queue_prop->type == QUEUE_TYPE_NA) { + dev_err(hdev->dev, "Queue index %d is invalid\n", + chunk->queue_index); + return -EINVAL; + } + + if (hw_queue_prop->driver_only) { + dev_err(hdev->dev, + "Queue index %d is restricted for the kernel driver\n", + chunk->queue_index); + return -EINVAL; + } + + *queue_type = hw_queue_prop->type; + *is_kernel_allocated_cb = !!hw_queue_prop->requires_kernel_cb; + + return 0; +} + +static struct hl_cb *get_cb_from_cs_chunk(struct hl_device *hdev, + struct hl_cb_mgr *cb_mgr, + struct hl_cs_chunk *chunk) +{ + struct hl_cb *cb; + u32 cb_handle; + + cb_handle = (u32) (chunk->cb_handle >> PAGE_SHIFT); + + cb = hl_cb_get(hdev, cb_mgr, cb_handle); + if (!cb) { + dev_err(hdev->dev, "CB handle 0x%x invalid\n", cb_handle); + return NULL; + } + + if ((chunk->cb_size < 8) || (chunk->cb_size > cb->size)) { + dev_err(hdev->dev, "CB size %u invalid\n", chunk->cb_size); + goto release_cb; + } + + spin_lock(&cb->lock); + cb->cs_cnt++; + spin_unlock(&cb->lock); + + return cb; + +release_cb: + hl_cb_put(cb); + return NULL; +} + +struct hl_cs_job *hl_cs_allocate_job(struct hl_device *hdev, + enum hl_queue_type queue_type, bool is_kernel_allocated_cb) +{ + struct hl_cs_job *job; + + job = kzalloc(sizeof(*job), GFP_ATOMIC); + if (!job) + return NULL; + + job->queue_type = queue_type; + job->is_kernel_allocated_cb = is_kernel_allocated_cb; + + if (is_cb_patched(hdev, job)) + INIT_LIST_HEAD(&job->userptr_list); + + if (job->queue_type == QUEUE_TYPE_EXT) + INIT_WORK(&job->finish_work, job_wq_completion); + + return job; +} + +static int cs_ioctl_default(struct hl_fpriv *hpriv, void __user *chunks, + u32 num_chunks, u64 *cs_seq) +{ + struct hl_device *hdev = hpriv->hdev; + struct hl_cs_chunk *cs_chunk_array; + struct hl_cs_job *job; + struct hl_cs *cs; + struct hl_cb *cb; + bool int_queues_only = true; + u32 size_to_copy; + int rc, i; + + *cs_seq = ULLONG_MAX; + + if (num_chunks > HL_MAX_JOBS_PER_CS) { + dev_err(hdev->dev, + "Number of chunks can NOT be larger than %d\n", + HL_MAX_JOBS_PER_CS); + rc = -EINVAL; + goto out; + } + + cs_chunk_array = kmalloc_array(num_chunks, sizeof(*cs_chunk_array), + GFP_ATOMIC); + if (!cs_chunk_array) { + rc = -ENOMEM; + goto out; + } + + size_to_copy = num_chunks * sizeof(struct hl_cs_chunk); + if (copy_from_user(cs_chunk_array, chunks, size_to_copy)) { + dev_err(hdev->dev, "Failed to copy cs chunk array from user\n"); + rc = -EFAULT; + goto free_cs_chunk_array; + } + + /* increment refcnt for context */ + hl_ctx_get(hdev, hpriv->ctx); + + rc = allocate_cs(hdev, hpriv->ctx, CS_TYPE_DEFAULT, &cs); + if (rc) { + hl_ctx_put(hpriv->ctx); + goto free_cs_chunk_array; + } + + *cs_seq = cs->sequence; + + hl_debugfs_add_cs(cs); + + /* Validate ALL the CS chunks before submitting the CS */ + for (i = 0 ; i < num_chunks ; i++) { + struct hl_cs_chunk *chunk = &cs_chunk_array[i]; + enum hl_queue_type queue_type; + bool is_kernel_allocated_cb; + + rc = validate_queue_index(hdev, chunk, &queue_type, + &is_kernel_allocated_cb); + if (rc) { + hpriv->ctx->cs_counters.parsing_drop_cnt++; + goto free_cs_object; + } + + if (is_kernel_allocated_cb) { + cb = get_cb_from_cs_chunk(hdev, &hpriv->cb_mgr, chunk); + if (!cb) { + hpriv->ctx->cs_counters.parsing_drop_cnt++; + rc = -EINVAL; + goto free_cs_object; + } + } else { + cb = (struct hl_cb *) (uintptr_t) chunk->cb_handle; + } + + if (queue_type == QUEUE_TYPE_EXT || queue_type == QUEUE_TYPE_HW) + int_queues_only = false; + + job = hl_cs_allocate_job(hdev, queue_type, + is_kernel_allocated_cb); + if (!job) { + hpriv->ctx->cs_counters.out_of_mem_drop_cnt++; + dev_err(hdev->dev, "Failed to allocate a new job\n"); + rc = -ENOMEM; + if (is_kernel_allocated_cb) + goto release_cb; + else + goto free_cs_object; + } + + job->id = i + 1; + job->cs = cs; + job->user_cb = cb; + job->user_cb_size = chunk->cb_size; + job->hw_queue_id = chunk->queue_index; + + cs->jobs_in_queue_cnt[job->hw_queue_id]++; + + list_add_tail(&job->cs_node, &cs->job_list); + + /* + * Increment CS reference. When CS reference is 0, CS is + * done and can be signaled to user and free all its resources + * Only increment for JOB on external or H/W queues, because + * only for those JOBs we get completion + */ + if (job->queue_type == QUEUE_TYPE_EXT || + job->queue_type == QUEUE_TYPE_HW) + cs_get(cs); + + hl_debugfs_add_job(hdev, job); + + rc = cs_parser(hpriv, job); + if (rc) { + hpriv->ctx->cs_counters.parsing_drop_cnt++; + dev_err(hdev->dev, + "Failed to parse JOB %d.%llu.%d, err %d, rejecting the CS\n", + cs->ctx->asid, cs->sequence, job->id, rc); + goto free_cs_object; + } + } + + if (int_queues_only) { + hpriv->ctx->cs_counters.parsing_drop_cnt++; + dev_err(hdev->dev, + "Reject CS %d.%llu because only internal queues jobs are present\n", + cs->ctx->asid, cs->sequence); + rc = -EINVAL; + goto free_cs_object; + } + + rc = hl_hw_queue_schedule_cs(cs); + if (rc) { + if (rc != -EAGAIN) + dev_err(hdev->dev, + "Failed to submit CS %d.%llu to H/W queues, error %d\n", + cs->ctx->asid, cs->sequence, rc); + goto free_cs_object; + } + + rc = HL_CS_STATUS_SUCCESS; + goto put_cs; + +release_cb: + spin_lock(&cb->lock); + cb->cs_cnt--; + spin_unlock(&cb->lock); + hl_cb_put(cb); +free_cs_object: + cs_rollback(hdev, cs); + *cs_seq = ULLONG_MAX; + /* The path below is both for good and erroneous exits */ +put_cs: + /* We finished with the CS in this function, so put the ref */ + cs_put(cs); +free_cs_chunk_array: + kfree(cs_chunk_array); +out: + return rc; +} + +static int cs_ioctl_signal_wait(struct hl_fpriv *hpriv, enum hl_cs_type cs_type, + void __user *chunks, u32 num_chunks, + u64 *cs_seq) +{ + struct hl_device *hdev = hpriv->hdev; + struct hl_ctx *ctx = hpriv->ctx; + struct hl_cs_chunk *cs_chunk_array, *chunk; + struct hw_queue_properties *hw_queue_prop; + struct dma_fence *sig_fence = NULL; + struct hl_cs_job *job; + struct hl_cs *cs; + struct hl_cb *cb; + enum hl_queue_type q_type; + u64 *signal_seq_arr = NULL, signal_seq; + u32 size_to_copy, q_idx, signal_seq_arr_len, cb_size; + int rc; + + *cs_seq = ULLONG_MAX; + + if (num_chunks > HL_MAX_JOBS_PER_CS) { + dev_err(hdev->dev, + "Number of chunks can NOT be larger than %d\n", + HL_MAX_JOBS_PER_CS); + rc = -EINVAL; + goto out; + } + + cs_chunk_array = kmalloc_array(num_chunks, sizeof(*cs_chunk_array), + GFP_ATOMIC); + if (!cs_chunk_array) { + rc = -ENOMEM; + goto out; + } + + size_to_copy = num_chunks * sizeof(struct hl_cs_chunk); + if (copy_from_user(cs_chunk_array, chunks, size_to_copy)) { + dev_err(hdev->dev, "Failed to copy cs chunk array from user\n"); + rc = -EFAULT; + goto free_cs_chunk_array; + } + + /* currently it is guaranteed to have only one chunk */ + chunk = &cs_chunk_array[0]; + q_idx = chunk->queue_index; + hw_queue_prop = &hdev->asic_prop.hw_queues_props[q_idx]; + q_type = hw_queue_prop->type; + + if ((q_idx >= hdev->asic_prop.max_queues) || + (!hw_queue_prop->supports_sync_stream)) { + dev_err(hdev->dev, "Queue index %d is invalid\n", q_idx); + rc = -EINVAL; + goto free_cs_chunk_array; + } + + if (cs_type == CS_TYPE_WAIT) { + struct hl_cs_compl *sig_waitcs_cmpl; + + signal_seq_arr_len = chunk->num_signal_seq_arr; + + /* currently only one signal seq is supported */ + if (signal_seq_arr_len != 1) { + dev_err(hdev->dev, + "Wait for signal CS supports only one signal CS seq\n"); + rc = -EINVAL; + goto free_cs_chunk_array; + } + + signal_seq_arr = kmalloc_array(signal_seq_arr_len, + sizeof(*signal_seq_arr), + GFP_ATOMIC); + if (!signal_seq_arr) { + rc = -ENOMEM; + goto free_cs_chunk_array; + } + + size_to_copy = chunk->num_signal_seq_arr * + sizeof(*signal_seq_arr); + if (copy_from_user(signal_seq_arr, + u64_to_user_ptr(chunk->signal_seq_arr), + size_to_copy)) { + dev_err(hdev->dev, + "Failed to copy signal seq array from user\n"); + rc = -EFAULT; + goto free_signal_seq_array; + } + + /* currently it is guaranteed to have only one signal seq */ + signal_seq = signal_seq_arr[0]; + sig_fence = hl_ctx_get_fence(ctx, signal_seq); + if (IS_ERR(sig_fence)) { + dev_err(hdev->dev, + "Failed to get signal CS with seq 0x%llx\n", + signal_seq); + rc = PTR_ERR(sig_fence); + goto free_signal_seq_array; + } + + if (!sig_fence) { + /* signal CS already finished */ + rc = 0; + goto free_signal_seq_array; + } + + sig_waitcs_cmpl = + container_of(sig_fence, struct hl_cs_compl, base_fence); + + if (sig_waitcs_cmpl->type != CS_TYPE_SIGNAL) { + dev_err(hdev->dev, + "CS seq 0x%llx is not of a signal CS\n", + signal_seq); + dma_fence_put(sig_fence); + rc = -EINVAL; + goto free_signal_seq_array; + } + + if (dma_fence_is_signaled(sig_fence)) { + /* signal CS already finished */ + dma_fence_put(sig_fence); + rc = 0; + goto free_signal_seq_array; + } + } + + /* increment refcnt for context */ + hl_ctx_get(hdev, ctx); + + rc = allocate_cs(hdev, ctx, cs_type, &cs); + if (rc) { + if (cs_type == CS_TYPE_WAIT) + dma_fence_put(sig_fence); + hl_ctx_put(ctx); + goto free_signal_seq_array; + } + + /* + * Save the signal CS fence for later initialization right before + * hanging the wait CS on the queue. + */ + if (cs->type == CS_TYPE_WAIT) + cs->signal_fence = sig_fence; + + hl_debugfs_add_cs(cs); + + *cs_seq = cs->sequence; + + job = hl_cs_allocate_job(hdev, q_type, true); + if (!job) { + ctx->cs_counters.out_of_mem_drop_cnt++; + dev_err(hdev->dev, "Failed to allocate a new job\n"); + rc = -ENOMEM; + goto put_cs; + } + + cb = hl_cb_kernel_create(hdev, PAGE_SIZE); + if (!cb) { + ctx->cs_counters.out_of_mem_drop_cnt++; + kfree(job); + rc = -EFAULT; + goto put_cs; + } + + if (cs->type == CS_TYPE_WAIT) + cb_size = hdev->asic_funcs->get_wait_cb_size(hdev); + else + cb_size = hdev->asic_funcs->get_signal_cb_size(hdev); + + job->id = 0; + job->cs = cs; + job->user_cb = cb; + job->user_cb->cs_cnt++; + job->user_cb_size = cb_size; + job->hw_queue_id = q_idx; + + /* + * No need in parsing, user CB is the patched CB. + * We call hl_cb_destroy() out of two reasons - we don't need the CB in + * the CB idr anymore and to decrement its refcount as it was + * incremented inside hl_cb_kernel_create(). + */ + job->patched_cb = job->user_cb; + job->job_cb_size = job->user_cb_size; + hl_cb_destroy(hdev, &hdev->kernel_cb_mgr, cb->id << PAGE_SHIFT); + + cs->jobs_in_queue_cnt[job->hw_queue_id]++; + + list_add_tail(&job->cs_node, &cs->job_list); + + /* increment refcount as for external queues we get completion */ + cs_get(cs); + + hl_debugfs_add_job(hdev, job); + + rc = hl_hw_queue_schedule_cs(cs); + if (rc) { + if (rc != -EAGAIN) + dev_err(hdev->dev, + "Failed to submit CS %d.%llu to H/W queues, error %d\n", + ctx->asid, cs->sequence, rc); + goto free_cs_object; + } + + rc = HL_CS_STATUS_SUCCESS; + goto put_cs; + +free_cs_object: + cs_rollback(hdev, cs); + *cs_seq = ULLONG_MAX; + /* The path below is both for good and erroneous exits */ +put_cs: + /* We finished with the CS in this function, so put the ref */ + cs_put(cs); +free_signal_seq_array: + if (cs_type == CS_TYPE_WAIT) + kfree(signal_seq_arr); +free_cs_chunk_array: + kfree(cs_chunk_array); +out: + return rc; +} + +int hl_cs_ioctl(struct hl_fpriv *hpriv, void *data) +{ + struct hl_device *hdev = hpriv->hdev; + union hl_cs_args *args = data; + struct hl_ctx *ctx = hpriv->ctx; + void __user *chunks_execute, *chunks_restore; + enum hl_cs_type cs_type; + u32 num_chunks_execute, num_chunks_restore, sig_wait_flags; + u64 cs_seq = ULONG_MAX; + int rc, do_ctx_switch; + bool need_soft_reset = false; + + if (hl_device_disabled_or_in_reset(hdev)) { + dev_warn_ratelimited(hdev->dev, + "Device is %s. Can't submit new CS\n", + atomic_read(&hdev->in_reset) ? "in_reset" : "disabled"); + rc = -EBUSY; + goto out; + } + + sig_wait_flags = args->in.cs_flags & HL_CS_FLAGS_SIG_WAIT; + + if (unlikely(sig_wait_flags == HL_CS_FLAGS_SIG_WAIT)) { + dev_err(hdev->dev, + "Signal and wait CS flags are mutually exclusive, context %d\n", + ctx->asid); + rc = -EINVAL; + goto out; + } + + if (unlikely((sig_wait_flags & HL_CS_FLAGS_SIG_WAIT) && + (!hdev->supports_sync_stream))) { + dev_err(hdev->dev, "Sync stream CS is not supported\n"); + rc = -EINVAL; + goto out; + } + + if (args->in.cs_flags & HL_CS_FLAGS_SIGNAL) + cs_type = CS_TYPE_SIGNAL; + else if (args->in.cs_flags & HL_CS_FLAGS_WAIT) + cs_type = CS_TYPE_WAIT; + else + cs_type = CS_TYPE_DEFAULT; + + chunks_execute = (void __user *) (uintptr_t) args->in.chunks_execute; + num_chunks_execute = args->in.num_chunks_execute; + + if (cs_type == CS_TYPE_DEFAULT) { + if (!num_chunks_execute) { + dev_err(hdev->dev, + "Got execute CS with 0 chunks, context %d\n", + ctx->asid); + rc = -EINVAL; + goto out; + } + } else if (num_chunks_execute != 1) { + dev_err(hdev->dev, + "Sync stream CS mandates one chunk only, context %d\n", + ctx->asid); + rc = -EINVAL; + goto out; + } + + do_ctx_switch = atomic_cmpxchg(&ctx->thread_ctx_switch_token, 1, 0); + + if (do_ctx_switch || (args->in.cs_flags & HL_CS_FLAGS_FORCE_RESTORE)) { + long ret; + + chunks_restore = + (void __user *) (uintptr_t) args->in.chunks_restore; + num_chunks_restore = args->in.num_chunks_restore; + + mutex_lock(&hpriv->restore_phase_mutex); + + if (do_ctx_switch) { + rc = hdev->asic_funcs->context_switch(hdev, ctx->asid); + if (rc) { + dev_err_ratelimited(hdev->dev, + "Failed to switch to context %d, rejecting CS! %d\n", + ctx->asid, rc); + /* + * If we timedout, or if the device is not IDLE + * while we want to do context-switch (-EBUSY), + * we need to soft-reset because QMAN is + * probably stuck. However, we can't call to + * reset here directly because of deadlock, so + * need to do it at the very end of this + * function + */ + if ((rc == -ETIMEDOUT) || (rc == -EBUSY)) + need_soft_reset = true; + mutex_unlock(&hpriv->restore_phase_mutex); + goto out; + } + } + + hdev->asic_funcs->restore_phase_topology(hdev); + + if (!num_chunks_restore) { + dev_dbg(hdev->dev, + "Need to run restore phase but restore CS is empty\n"); + rc = 0; + } else { + rc = cs_ioctl_default(hpriv, chunks_restore, + num_chunks_restore, &cs_seq); + } + + mutex_unlock(&hpriv->restore_phase_mutex); + + if (rc) { + dev_err(hdev->dev, + "Failed to submit restore CS for context %d (%d)\n", + ctx->asid, rc); + goto out; + } + + /* Need to wait for restore completion before execution phase */ + if (num_chunks_restore) { + ret = _hl_cs_wait_ioctl(hdev, ctx, + jiffies_to_usecs(hdev->timeout_jiffies), + cs_seq); + if (ret <= 0) { + dev_err(hdev->dev, + "Restore CS for context %d failed to complete %ld\n", + ctx->asid, ret); + rc = -ENOEXEC; + goto out; + } + } + + ctx->thread_ctx_switch_wait_token = 1; + } else if (!ctx->thread_ctx_switch_wait_token) { + u32 tmp; + + rc = hl_poll_timeout_memory(hdev, + &ctx->thread_ctx_switch_wait_token, tmp, (tmp == 1), + 100, jiffies_to_usecs(hdev->timeout_jiffies), false); + + if (rc == -ETIMEDOUT) { + dev_err(hdev->dev, + "context switch phase timeout (%d)\n", tmp); + goto out; + } + } + + if (cs_type == CS_TYPE_DEFAULT) + rc = cs_ioctl_default(hpriv, chunks_execute, num_chunks_execute, + &cs_seq); + else + rc = cs_ioctl_signal_wait(hpriv, cs_type, chunks_execute, + num_chunks_execute, &cs_seq); + +out: + if (rc != -EAGAIN) { + memset(args, 0, sizeof(*args)); + args->out.status = rc; + args->out.seq = cs_seq; + } + + if (((rc == -ETIMEDOUT) || (rc == -EBUSY)) && (need_soft_reset)) + hl_device_reset(hdev, false, false); + + return rc; +} + +static long _hl_cs_wait_ioctl(struct hl_device *hdev, + struct hl_ctx *ctx, u64 timeout_us, u64 seq) +{ + struct dma_fence *fence; + unsigned long timeout; + long rc; + + if (timeout_us == MAX_SCHEDULE_TIMEOUT) + timeout = timeout_us; + else + timeout = usecs_to_jiffies(timeout_us); + + hl_ctx_get(hdev, ctx); + + fence = hl_ctx_get_fence(ctx, seq); + if (IS_ERR(fence)) { + rc = PTR_ERR(fence); + if (rc == -EINVAL) + dev_notice_ratelimited(hdev->dev, + "Can't wait on CS %llu because current CS is at seq %llu\n", + seq, ctx->cs_sequence); + } else if (fence) { + rc = dma_fence_wait_timeout(fence, true, timeout); + if (fence->error == -ETIMEDOUT) + rc = -ETIMEDOUT; + else if (fence->error == -EIO) + rc = -EIO; + dma_fence_put(fence); + } else { + dev_dbg(hdev->dev, + "Can't wait on seq %llu because current CS is at seq %llu (Fence is gone)\n", + seq, ctx->cs_sequence); + rc = 1; + } + + hl_ctx_put(ctx); + + return rc; +} + +int hl_cs_wait_ioctl(struct hl_fpriv *hpriv, void *data) +{ + struct hl_device *hdev = hpriv->hdev; + union hl_wait_cs_args *args = data; + u64 seq = args->in.seq; + long rc; + + rc = _hl_cs_wait_ioctl(hdev, hpriv->ctx, args->in.timeout_us, seq); + + memset(args, 0, sizeof(*args)); + + if (rc < 0) { + if (rc == -ERESTARTSYS) { + dev_err_ratelimited(hdev->dev, + "user process got signal while waiting for CS handle %llu\n", + seq); + args->out.status = HL_WAIT_CS_STATUS_INTERRUPTED; + rc = -EINTR; + } else if (rc == -ETIMEDOUT) { + dev_err_ratelimited(hdev->dev, + "CS %llu has timed-out while user process is waiting for it\n", + seq); + args->out.status = HL_WAIT_CS_STATUS_TIMEDOUT; + } else if (rc == -EIO) { + dev_err_ratelimited(hdev->dev, + "CS %llu has been aborted while user process is waiting for it\n", + seq); + args->out.status = HL_WAIT_CS_STATUS_ABORTED; + } + return rc; + } + + if (rc == 0) + args->out.status = HL_WAIT_CS_STATUS_BUSY; + else + args->out.status = HL_WAIT_CS_STATUS_COMPLETED; + + return 0; +} diff --git a/drivers/misc/habanalabs/common/context.c b/drivers/misc/habanalabs/common/context.c new file mode 100644 index 000000000000..1e3e5b19ecd9 --- /dev/null +++ b/drivers/misc/habanalabs/common/context.c @@ -0,0 +1,237 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright 2016-2019 HabanaLabs, Ltd. + * All Rights Reserved. + */ + +#include "habanalabs.h" + +#include + +static void hl_ctx_fini(struct hl_ctx *ctx) +{ + struct hl_device *hdev = ctx->hdev; + int i; + + /* + * If we arrived here, there are no jobs waiting for this context + * on its queues so we can safely remove it. + * This is because for each CS, we increment the ref count and for + * every CS that was finished we decrement it and we won't arrive + * to this function unless the ref count is 0 + */ + + for (i = 0 ; i < hdev->asic_prop.max_pending_cs ; i++) + dma_fence_put(ctx->cs_pending[i]); + + kfree(ctx->cs_pending); + + if (ctx->asid != HL_KERNEL_ASID_ID) { + /* The engines are stopped as there is no executing CS, but the + * Coresight might be still working by accessing addresses + * related to the stopped engines. Hence stop it explicitly. + * Stop only if this is the compute context, as there can be + * only one compute context + */ + if ((hdev->in_debug) && (hdev->compute_ctx == ctx)) + hl_device_set_debug_mode(hdev, false); + + hl_vm_ctx_fini(ctx); + hl_asid_free(hdev, ctx->asid); + } else { + hl_mmu_ctx_fini(ctx); + } +} + +void hl_ctx_do_release(struct kref *ref) +{ + struct hl_ctx *ctx; + + ctx = container_of(ref, struct hl_ctx, refcount); + + hl_ctx_fini(ctx); + + if (ctx->hpriv) + hl_hpriv_put(ctx->hpriv); + + kfree(ctx); +} + +int hl_ctx_create(struct hl_device *hdev, struct hl_fpriv *hpriv) +{ + struct hl_ctx_mgr *mgr = &hpriv->ctx_mgr; + struct hl_ctx *ctx; + int rc; + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) { + rc = -ENOMEM; + goto out_err; + } + + mutex_lock(&mgr->ctx_lock); + rc = idr_alloc(&mgr->ctx_handles, ctx, 1, 0, GFP_KERNEL); + mutex_unlock(&mgr->ctx_lock); + + if (rc < 0) { + dev_err(hdev->dev, "Failed to allocate IDR for a new CTX\n"); + goto free_ctx; + } + + ctx->handle = rc; + + rc = hl_ctx_init(hdev, ctx, false); + if (rc) + goto remove_from_idr; + + hl_hpriv_get(hpriv); + ctx->hpriv = hpriv; + + /* TODO: remove for multiple contexts per process */ + hpriv->ctx = ctx; + + /* TODO: remove the following line for multiple process support */ + hdev->compute_ctx = ctx; + + return 0; + +remove_from_idr: + mutex_lock(&mgr->ctx_lock); + idr_remove(&mgr->ctx_handles, ctx->handle); + mutex_unlock(&mgr->ctx_lock); +free_ctx: + kfree(ctx); +out_err: + return rc; +} + +void hl_ctx_free(struct hl_device *hdev, struct hl_ctx *ctx) +{ + if (kref_put(&ctx->refcount, hl_ctx_do_release) == 1) + return; + + dev_warn(hdev->dev, + "user process released device but its command submissions are still executing\n"); +} + +int hl_ctx_init(struct hl_device *hdev, struct hl_ctx *ctx, bool is_kernel_ctx) +{ + int rc = 0; + + ctx->hdev = hdev; + + kref_init(&ctx->refcount); + + ctx->cs_sequence = 1; + spin_lock_init(&ctx->cs_lock); + atomic_set(&ctx->thread_ctx_switch_token, 1); + ctx->thread_ctx_switch_wait_token = 0; + ctx->cs_pending = kcalloc(hdev->asic_prop.max_pending_cs, + sizeof(struct dma_fence *), + GFP_KERNEL); + if (!ctx->cs_pending) + return -ENOMEM; + + if (is_kernel_ctx) { + ctx->asid = HL_KERNEL_ASID_ID; /* Kernel driver gets ASID 0 */ + rc = hl_mmu_ctx_init(ctx); + if (rc) { + dev_err(hdev->dev, "Failed to init mmu ctx module\n"); + goto mem_ctx_err; + } + } else { + ctx->asid = hl_asid_alloc(hdev); + if (!ctx->asid) { + dev_err(hdev->dev, "No free ASID, failed to create context\n"); + return -ENOMEM; + } + + rc = hl_vm_ctx_init(ctx); + if (rc) { + dev_err(hdev->dev, "Failed to init mem ctx module\n"); + rc = -ENOMEM; + goto mem_ctx_err; + } + } + + return 0; + +mem_ctx_err: + if (ctx->asid != HL_KERNEL_ASID_ID) + hl_asid_free(hdev, ctx->asid); + + return rc; +} + +void hl_ctx_get(struct hl_device *hdev, struct hl_ctx *ctx) +{ + kref_get(&ctx->refcount); +} + +int hl_ctx_put(struct hl_ctx *ctx) +{ + return kref_put(&ctx->refcount, hl_ctx_do_release); +} + +struct dma_fence *hl_ctx_get_fence(struct hl_ctx *ctx, u64 seq) +{ + struct asic_fixed_properties *asic_prop = &ctx->hdev->asic_prop; + struct dma_fence *fence; + + spin_lock(&ctx->cs_lock); + + if (seq >= ctx->cs_sequence) { + spin_unlock(&ctx->cs_lock); + return ERR_PTR(-EINVAL); + } + + if (seq + asic_prop->max_pending_cs < ctx->cs_sequence) { + spin_unlock(&ctx->cs_lock); + return NULL; + } + + fence = dma_fence_get( + ctx->cs_pending[seq & (asic_prop->max_pending_cs - 1)]); + spin_unlock(&ctx->cs_lock); + + return fence; +} + +/* + * hl_ctx_mgr_init - initialize the context manager + * + * @mgr: pointer to context manager structure + * + * This manager is an object inside the hpriv object of the user process. + * The function is called when a user process opens the FD. + */ +void hl_ctx_mgr_init(struct hl_ctx_mgr *mgr) +{ + mutex_init(&mgr->ctx_lock); + idr_init(&mgr->ctx_handles); +} + +/* + * hl_ctx_mgr_fini - finalize the context manager + * + * @hdev: pointer to device structure + * @mgr: pointer to context manager structure + * + * This function goes over all the contexts in the manager and frees them. + * It is called when a process closes the FD. + */ +void hl_ctx_mgr_fini(struct hl_device *hdev, struct hl_ctx_mgr *mgr) +{ + struct hl_ctx *ctx; + struct idr *idp; + u32 id; + + idp = &mgr->ctx_handles; + + idr_for_each_entry(idp, ctx, id) + hl_ctx_free(hdev, ctx); + + idr_destroy(&mgr->ctx_handles); + mutex_destroy(&mgr->ctx_lock); +} diff --git a/drivers/misc/habanalabs/common/debugfs.c b/drivers/misc/habanalabs/common/debugfs.c new file mode 100644 index 000000000000..fc4372c18ce2 --- /dev/null +++ b/drivers/misc/habanalabs/common/debugfs.c @@ -0,0 +1,1411 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright 2016-2019 HabanaLabs, Ltd. + * All Rights Reserved. + */ + +#include "habanalabs.h" +#include "include/hw_ip/mmu/mmu_general.h" + +#include +#include +#include + +#define MMU_ADDR_BUF_SIZE 40 +#define MMU_ASID_BUF_SIZE 10 +#define MMU_KBUF_SIZE (MMU_ADDR_BUF_SIZE + MMU_ASID_BUF_SIZE) + +static struct dentry *hl_debug_root; + +static int hl_debugfs_i2c_read(struct hl_device *hdev, u8 i2c_bus, u8 i2c_addr, + u8 i2c_reg, u32 *val) +{ + struct armcp_packet pkt; + int rc; + + if (hl_device_disabled_or_in_reset(hdev)) + return -EBUSY; + + memset(&pkt, 0, sizeof(pkt)); + + pkt.ctl = cpu_to_le32(ARMCP_PACKET_I2C_RD << + ARMCP_PKT_CTL_OPCODE_SHIFT); + pkt.i2c_bus = i2c_bus; + pkt.i2c_addr = i2c_addr; + pkt.i2c_reg = i2c_reg; + + rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), + HL_DEVICE_TIMEOUT_USEC, (long *) val); + + if (rc) + dev_err(hdev->dev, "Failed to read from I2C, error %d\n", rc); + + return rc; +} + +static int hl_debugfs_i2c_write(struct hl_device *hdev, u8 i2c_bus, u8 i2c_addr, + u8 i2c_reg, u32 val) +{ + struct armcp_packet pkt; + int rc; + + if (hl_device_disabled_or_in_reset(hdev)) + return -EBUSY; + + memset(&pkt, 0, sizeof(pkt)); + + pkt.ctl = cpu_to_le32(ARMCP_PACKET_I2C_WR << + ARMCP_PKT_CTL_OPCODE_SHIFT); + pkt.i2c_bus = i2c_bus; + pkt.i2c_addr = i2c_addr; + pkt.i2c_reg = i2c_reg; + pkt.value = cpu_to_le64(val); + + rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), + HL_DEVICE_TIMEOUT_USEC, NULL); + + if (rc) + dev_err(hdev->dev, "Failed to write to I2C, error %d\n", rc); + + return rc; +} + +static void hl_debugfs_led_set(struct hl_device *hdev, u8 led, u8 state) +{ + struct armcp_packet pkt; + int rc; + + if (hl_device_disabled_or_in_reset(hdev)) + return; + + memset(&pkt, 0, sizeof(pkt)); + + pkt.ctl = cpu_to_le32(ARMCP_PACKET_LED_SET << + ARMCP_PKT_CTL_OPCODE_SHIFT); + pkt.led_index = cpu_to_le32(led); + pkt.value = cpu_to_le64(state); + + rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), + HL_DEVICE_TIMEOUT_USEC, NULL); + + if (rc) + dev_err(hdev->dev, "Failed to set LED %d, error %d\n", led, rc); +} + +static int command_buffers_show(struct seq_file *s, void *data) +{ + struct hl_debugfs_entry *entry = s->private; + struct hl_dbg_device_entry *dev_entry = entry->dev_entry; + struct hl_cb *cb; + bool first = true; + + spin_lock(&dev_entry->cb_spinlock); + + list_for_each_entry(cb, &dev_entry->cb_list, debugfs_list) { + if (first) { + first = false; + seq_puts(s, "\n"); + seq_puts(s, " CB ID CTX ID CB size CB RefCnt mmap? CS counter\n"); + seq_puts(s, "---------------------------------------------------------------\n"); + } + seq_printf(s, + " %03d %d 0x%08x %d %d %d\n", + cb->id, cb->ctx_id, cb->size, + kref_read(&cb->refcount), + cb->mmap, cb->cs_cnt); + } + + spin_unlock(&dev_entry->cb_spinlock); + + if (!first) + seq_puts(s, "\n"); + + return 0; +} + +static int command_submission_show(struct seq_file *s, void *data) +{ + struct hl_debugfs_entry *entry = s->private; + struct hl_dbg_device_entry *dev_entry = entry->dev_entry; + struct hl_cs *cs; + bool first = true; + + spin_lock(&dev_entry->cs_spinlock); + + list_for_each_entry(cs, &dev_entry->cs_list, debugfs_list) { + if (first) { + first = false; + seq_puts(s, "\n"); + seq_puts(s, " CS ID CTX ASID CS RefCnt Submitted Completed\n"); + seq_puts(s, "------------------------------------------------------\n"); + } + seq_printf(s, + " %llu %d %d %d %d\n", + cs->sequence, cs->ctx->asid, + kref_read(&cs->refcount), + cs->submitted, cs->completed); + } + + spin_unlock(&dev_entry->cs_spinlock); + + if (!first) + seq_puts(s, "\n"); + + return 0; +} + +static int command_submission_jobs_show(struct seq_file *s, void *data) +{ + struct hl_debugfs_entry *entry = s->private; + struct hl_dbg_device_entry *dev_entry = entry->dev_entry; + struct hl_cs_job *job; + bool first = true; + + spin_lock(&dev_entry->cs_job_spinlock); + + list_for_each_entry(job, &dev_entry->cs_job_list, debugfs_list) { + if (first) { + first = false; + seq_puts(s, "\n"); + seq_puts(s, " JOB ID CS ID CTX ASID H/W Queue\n"); + seq_puts(s, "---------------------------------------\n"); + } + if (job->cs) + seq_printf(s, + " %02d %llu %d %d\n", + job->id, job->cs->sequence, job->cs->ctx->asid, + job->hw_queue_id); + else + seq_printf(s, + " %02d 0 %d %d\n", + job->id, HL_KERNEL_ASID_ID, job->hw_queue_id); + } + + spin_unlock(&dev_entry->cs_job_spinlock); + + if (!first) + seq_puts(s, "\n"); + + return 0; +} + +static int userptr_show(struct seq_file *s, void *data) +{ + struct hl_debugfs_entry *entry = s->private; + struct hl_dbg_device_entry *dev_entry = entry->dev_entry; + struct hl_userptr *userptr; + char dma_dir[4][30] = {"DMA_BIDIRECTIONAL", "DMA_TO_DEVICE", + "DMA_FROM_DEVICE", "DMA_NONE"}; + bool first = true; + + spin_lock(&dev_entry->userptr_spinlock); + + list_for_each_entry(userptr, &dev_entry->userptr_list, debugfs_list) { + if (first) { + first = false; + seq_puts(s, "\n"); + seq_puts(s, " user virtual address size dma dir\n"); + seq_puts(s, "----------------------------------------------------------\n"); + } + seq_printf(s, + " 0x%-14llx %-10u %-30s\n", + userptr->addr, userptr->size, dma_dir[userptr->dir]); + } + + spin_unlock(&dev_entry->userptr_spinlock); + + if (!first) + seq_puts(s, "\n"); + + return 0; +} + +static int vm_show(struct seq_file *s, void *data) +{ + struct hl_debugfs_entry *entry = s->private; + struct hl_dbg_device_entry *dev_entry = entry->dev_entry; + struct hl_ctx *ctx; + struct hl_vm *vm; + struct hl_vm_hash_node *hnode; + struct hl_userptr *userptr; + struct hl_vm_phys_pg_pack *phys_pg_pack = NULL; + enum vm_type_t *vm_type; + bool once = true; + u64 j; + int i; + + if (!dev_entry->hdev->mmu_enable) + return 0; + + spin_lock(&dev_entry->ctx_mem_hash_spinlock); + + list_for_each_entry(ctx, &dev_entry->ctx_mem_hash_list, debugfs_list) { + once = false; + seq_puts(s, "\n\n----------------------------------------------------"); + seq_puts(s, "\n----------------------------------------------------\n\n"); + seq_printf(s, "ctx asid: %u\n", ctx->asid); + + seq_puts(s, "\nmappings:\n\n"); + seq_puts(s, " virtual address size handle\n"); + seq_puts(s, "----------------------------------------------------\n"); + mutex_lock(&ctx->mem_hash_lock); + hash_for_each(ctx->mem_hash, i, hnode, node) { + vm_type = hnode->ptr; + + if (*vm_type == VM_TYPE_USERPTR) { + userptr = hnode->ptr; + seq_printf(s, + " 0x%-14llx %-10u\n", + hnode->vaddr, userptr->size); + } else { + phys_pg_pack = hnode->ptr; + seq_printf(s, + " 0x%-14llx %-10llu %-4u\n", + hnode->vaddr, phys_pg_pack->total_size, + phys_pg_pack->handle); + } + } + mutex_unlock(&ctx->mem_hash_lock); + + vm = &ctx->hdev->vm; + spin_lock(&vm->idr_lock); + + if (!idr_is_empty(&vm->phys_pg_pack_handles)) + seq_puts(s, "\n\nallocations:\n"); + + idr_for_each_entry(&vm->phys_pg_pack_handles, phys_pg_pack, i) { + if (phys_pg_pack->asid != ctx->asid) + continue; + + seq_printf(s, "\nhandle: %u\n", phys_pg_pack->handle); + seq_printf(s, "page size: %u\n\n", + phys_pg_pack->page_size); + seq_puts(s, " physical address\n"); + seq_puts(s, "---------------------\n"); + for (j = 0 ; j < phys_pg_pack->npages ; j++) { + seq_printf(s, " 0x%-14llx\n", + phys_pg_pack->pages[j]); + } + } + spin_unlock(&vm->idr_lock); + + } + + spin_unlock(&dev_entry->ctx_mem_hash_spinlock); + + if (!once) + seq_puts(s, "\n"); + + return 0; +} + +/* these inline functions are copied from mmu.c */ +static inline u64 get_hop0_addr(struct hl_ctx *ctx) +{ + return ctx->hdev->asic_prop.mmu_pgt_addr + + (ctx->asid * ctx->hdev->asic_prop.mmu_hop_table_size); +} + +static inline u64 get_hopN_pte_addr(struct hl_ctx *ctx, u64 hop_addr, + u64 virt_addr, u64 mask, u64 shift) +{ + return hop_addr + ctx->hdev->asic_prop.mmu_pte_size * + ((virt_addr & mask) >> shift); +} + +static inline u64 get_hop0_pte_addr(struct hl_ctx *ctx, + struct hl_mmu_properties *mmu_specs, + u64 hop_addr, u64 vaddr) +{ + return get_hopN_pte_addr(ctx, hop_addr, vaddr, mmu_specs->hop0_mask, + mmu_specs->hop0_shift); +} + +static inline u64 get_hop1_pte_addr(struct hl_ctx *ctx, + struct hl_mmu_properties *mmu_specs, + u64 hop_addr, u64 vaddr) +{ + return get_hopN_pte_addr(ctx, hop_addr, vaddr, mmu_specs->hop1_mask, + mmu_specs->hop1_shift); +} + +static inline u64 get_hop2_pte_addr(struct hl_ctx *ctx, + struct hl_mmu_properties *mmu_specs, + u64 hop_addr, u64 vaddr) +{ + return get_hopN_pte_addr(ctx, hop_addr, vaddr, mmu_specs->hop2_mask, + mmu_specs->hop2_shift); +} + +static inline u64 get_hop3_pte_addr(struct hl_ctx *ctx, + struct hl_mmu_properties *mmu_specs, + u64 hop_addr, u64 vaddr) +{ + return get_hopN_pte_addr(ctx, hop_addr, vaddr, mmu_specs->hop3_mask, + mmu_specs->hop3_shift); +} + +static inline u64 get_hop4_pte_addr(struct hl_ctx *ctx, + struct hl_mmu_properties *mmu_specs, + u64 hop_addr, u64 vaddr) +{ + return get_hopN_pte_addr(ctx, hop_addr, vaddr, mmu_specs->hop4_mask, + mmu_specs->hop4_shift); +} + +static inline u64 get_next_hop_addr(u64 curr_pte) +{ + if (curr_pte & PAGE_PRESENT_MASK) + return curr_pte & HOP_PHYS_ADDR_MASK; + else + return ULLONG_MAX; +} + +static int mmu_show(struct seq_file *s, void *data) +{ + struct hl_debugfs_entry *entry = s->private; + struct hl_dbg_device_entry *dev_entry = entry->dev_entry; + struct hl_device *hdev = dev_entry->hdev; + struct asic_fixed_properties *prop = &hdev->asic_prop; + struct hl_mmu_properties *mmu_prop; + struct hl_ctx *ctx; + bool is_dram_addr; + + u64 hop0_addr = 0, hop0_pte_addr = 0, hop0_pte = 0, + hop1_addr = 0, hop1_pte_addr = 0, hop1_pte = 0, + hop2_addr = 0, hop2_pte_addr = 0, hop2_pte = 0, + hop3_addr = 0, hop3_pte_addr = 0, hop3_pte = 0, + hop4_addr = 0, hop4_pte_addr = 0, hop4_pte = 0, + virt_addr = dev_entry->mmu_addr; + + if (!hdev->mmu_enable) + return 0; + + if (dev_entry->mmu_asid == HL_KERNEL_ASID_ID) + ctx = hdev->kernel_ctx; + else + ctx = hdev->compute_ctx; + + if (!ctx) { + dev_err(hdev->dev, "no ctx available\n"); + return 0; + } + + is_dram_addr = hl_mem_area_inside_range(virt_addr, prop->dmmu.page_size, + prop->dmmu.start_addr, + prop->dmmu.end_addr); + + /* shifts and masks are the same in PMMU and HPMMU, use one of them */ + mmu_prop = is_dram_addr ? &prop->dmmu : &prop->pmmu; + + mutex_lock(&ctx->mmu_lock); + + /* the following lookup is copied from unmap() in mmu.c */ + + hop0_addr = get_hop0_addr(ctx); + hop0_pte_addr = get_hop0_pte_addr(ctx, mmu_prop, hop0_addr, virt_addr); + hop0_pte = hdev->asic_funcs->read_pte(hdev, hop0_pte_addr); + hop1_addr = get_next_hop_addr(hop0_pte); + + if (hop1_addr == ULLONG_MAX) + goto not_mapped; + + hop1_pte_addr = get_hop1_pte_addr(ctx, mmu_prop, hop1_addr, virt_addr); + hop1_pte = hdev->asic_funcs->read_pte(hdev, hop1_pte_addr); + hop2_addr = get_next_hop_addr(hop1_pte); + + if (hop2_addr == ULLONG_MAX) + goto not_mapped; + + hop2_pte_addr = get_hop2_pte_addr(ctx, mmu_prop, hop2_addr, virt_addr); + hop2_pte = hdev->asic_funcs->read_pte(hdev, hop2_pte_addr); + hop3_addr = get_next_hop_addr(hop2_pte); + + if (hop3_addr == ULLONG_MAX) + goto not_mapped; + + hop3_pte_addr = get_hop3_pte_addr(ctx, mmu_prop, hop3_addr, virt_addr); + hop3_pte = hdev->asic_funcs->read_pte(hdev, hop3_pte_addr); + + if (!(hop3_pte & LAST_MASK)) { + hop4_addr = get_next_hop_addr(hop3_pte); + + if (hop4_addr == ULLONG_MAX) + goto not_mapped; + + hop4_pte_addr = get_hop4_pte_addr(ctx, mmu_prop, hop4_addr, + virt_addr); + hop4_pte = hdev->asic_funcs->read_pte(hdev, hop4_pte_addr); + if (!(hop4_pte & PAGE_PRESENT_MASK)) + goto not_mapped; + } else { + if (!(hop3_pte & PAGE_PRESENT_MASK)) + goto not_mapped; + } + + seq_printf(s, "asid: %u, virt_addr: 0x%llx\n", + dev_entry->mmu_asid, dev_entry->mmu_addr); + + seq_printf(s, "hop0_addr: 0x%llx\n", hop0_addr); + seq_printf(s, "hop0_pte_addr: 0x%llx\n", hop0_pte_addr); + seq_printf(s, "hop0_pte: 0x%llx\n", hop0_pte); + + seq_printf(s, "hop1_addr: 0x%llx\n", hop1_addr); + seq_printf(s, "hop1_pte_addr: 0x%llx\n", hop1_pte_addr); + seq_printf(s, "hop1_pte: 0x%llx\n", hop1_pte); + + seq_printf(s, "hop2_addr: 0x%llx\n", hop2_addr); + seq_printf(s, "hop2_pte_addr: 0x%llx\n", hop2_pte_addr); + seq_printf(s, "hop2_pte: 0x%llx\n", hop2_pte); + + seq_printf(s, "hop3_addr: 0x%llx\n", hop3_addr); + seq_printf(s, "hop3_pte_addr: 0x%llx\n", hop3_pte_addr); + seq_printf(s, "hop3_pte: 0x%llx\n", hop3_pte); + + if (!(hop3_pte & LAST_MASK)) { + seq_printf(s, "hop4_addr: 0x%llx\n", hop4_addr); + seq_printf(s, "hop4_pte_addr: 0x%llx\n", hop4_pte_addr); + seq_printf(s, "hop4_pte: 0x%llx\n", hop4_pte); + } + + goto out; + +not_mapped: + dev_err(hdev->dev, "virt addr 0x%llx is not mapped to phys addr\n", + virt_addr); +out: + mutex_unlock(&ctx->mmu_lock); + + return 0; +} + +static ssize_t mmu_asid_va_write(struct file *file, const char __user *buf, + size_t count, loff_t *f_pos) +{ + struct seq_file *s = file->private_data; + struct hl_debugfs_entry *entry = s->private; + struct hl_dbg_device_entry *dev_entry = entry->dev_entry; + struct hl_device *hdev = dev_entry->hdev; + char kbuf[MMU_KBUF_SIZE]; + char *c; + ssize_t rc; + + if (!hdev->mmu_enable) + return count; + + if (count > sizeof(kbuf) - 1) + goto err; + if (copy_from_user(kbuf, buf, count)) + goto err; + kbuf[count] = 0; + + c = strchr(kbuf, ' '); + if (!c) + goto err; + *c = '\0'; + + rc = kstrtouint(kbuf, 10, &dev_entry->mmu_asid); + if (rc) + goto err; + + if (strncmp(c+1, "0x", 2)) + goto err; + rc = kstrtoull(c+3, 16, &dev_entry->mmu_addr); + if (rc) + goto err; + + return count; + +err: + dev_err(hdev->dev, "usage: echo <0xaddr> > mmu\n"); + + return -EINVAL; +} + +static int engines_show(struct seq_file *s, void *data) +{ + struct hl_debugfs_entry *entry = s->private; + struct hl_dbg_device_entry *dev_entry = entry->dev_entry; + struct hl_device *hdev = dev_entry->hdev; + + if (atomic_read(&hdev->in_reset)) { + dev_warn_ratelimited(hdev->dev, + "Can't check device idle during reset\n"); + return 0; + } + + hdev->asic_funcs->is_device_idle(hdev, NULL, s); + + return 0; +} + +static bool hl_is_device_va(struct hl_device *hdev, u64 addr) +{ + struct asic_fixed_properties *prop = &hdev->asic_prop; + + if (!hdev->mmu_enable) + goto out; + + if (hdev->dram_supports_virtual_memory && + (addr >= prop->dmmu.start_addr && addr < prop->dmmu.end_addr)) + return true; + + if (addr >= prop->pmmu.start_addr && + addr < prop->pmmu.end_addr) + return true; + + if (addr >= prop->pmmu_huge.start_addr && + addr < prop->pmmu_huge.end_addr) + return true; +out: + return false; +} + +static int device_va_to_pa(struct hl_device *hdev, u64 virt_addr, + u64 *phys_addr) +{ + struct hl_ctx *ctx = hdev->compute_ctx; + struct asic_fixed_properties *prop = &hdev->asic_prop; + struct hl_mmu_properties *mmu_prop; + u64 hop_addr, hop_pte_addr, hop_pte; + u64 offset_mask = HOP4_MASK | FLAGS_MASK; + int rc = 0; + bool is_dram_addr; + + if (!ctx) { + dev_err(hdev->dev, "no ctx available\n"); + return -EINVAL; + } + + is_dram_addr = hl_mem_area_inside_range(virt_addr, prop->dmmu.page_size, + prop->dmmu.start_addr, + prop->dmmu.end_addr); + + /* shifts and masks are the same in PMMU and HPMMU, use one of them */ + mmu_prop = is_dram_addr ? &prop->dmmu : &prop->pmmu; + + mutex_lock(&ctx->mmu_lock); + + /* hop 0 */ + hop_addr = get_hop0_addr(ctx); + hop_pte_addr = get_hop0_pte_addr(ctx, mmu_prop, hop_addr, virt_addr); + hop_pte = hdev->asic_funcs->read_pte(hdev, hop_pte_addr); + + /* hop 1 */ + hop_addr = get_next_hop_addr(hop_pte); + if (hop_addr == ULLONG_MAX) + goto not_mapped; + hop_pte_addr = get_hop1_pte_addr(ctx, mmu_prop, hop_addr, virt_addr); + hop_pte = hdev->asic_funcs->read_pte(hdev, hop_pte_addr); + + /* hop 2 */ + hop_addr = get_next_hop_addr(hop_pte); + if (hop_addr == ULLONG_MAX) + goto not_mapped; + hop_pte_addr = get_hop2_pte_addr(ctx, mmu_prop, hop_addr, virt_addr); + hop_pte = hdev->asic_funcs->read_pte(hdev, hop_pte_addr); + + /* hop 3 */ + hop_addr = get_next_hop_addr(hop_pte); + if (hop_addr == ULLONG_MAX) + goto not_mapped; + hop_pte_addr = get_hop3_pte_addr(ctx, mmu_prop, hop_addr, virt_addr); + hop_pte = hdev->asic_funcs->read_pte(hdev, hop_pte_addr); + + if (!(hop_pte & LAST_MASK)) { + /* hop 4 */ + hop_addr = get_next_hop_addr(hop_pte); + if (hop_addr == ULLONG_MAX) + goto not_mapped; + hop_pte_addr = get_hop4_pte_addr(ctx, mmu_prop, hop_addr, + virt_addr); + hop_pte = hdev->asic_funcs->read_pte(hdev, hop_pte_addr); + + offset_mask = FLAGS_MASK; + } + + if (!(hop_pte & PAGE_PRESENT_MASK)) + goto not_mapped; + + *phys_addr = (hop_pte & ~offset_mask) | (virt_addr & offset_mask); + + goto out; + +not_mapped: + dev_err(hdev->dev, "virt addr 0x%llx is not mapped to phys addr\n", + virt_addr); + rc = -EINVAL; +out: + mutex_unlock(&ctx->mmu_lock); + return rc; +} + +static ssize_t hl_data_read32(struct file *f, char __user *buf, + size_t count, loff_t *ppos) +{ + struct hl_dbg_device_entry *entry = file_inode(f)->i_private; + struct hl_device *hdev = entry->hdev; + char tmp_buf[32]; + u64 addr = entry->addr; + u32 val; + ssize_t rc; + + if (atomic_read(&hdev->in_reset)) { + dev_warn_ratelimited(hdev->dev, "Can't read during reset\n"); + return 0; + } + + if (*ppos) + return 0; + + if (hl_is_device_va(hdev, addr)) { + rc = device_va_to_pa(hdev, addr, &addr); + if (rc) + return rc; + } + + rc = hdev->asic_funcs->debugfs_read32(hdev, addr, &val); + if (rc) { + dev_err(hdev->dev, "Failed to read from 0x%010llx\n", addr); + return rc; + } + + sprintf(tmp_buf, "0x%08x\n", val); + return simple_read_from_buffer(buf, count, ppos, tmp_buf, + strlen(tmp_buf)); +} + +static ssize_t hl_data_write32(struct file *f, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct hl_dbg_device_entry *entry = file_inode(f)->i_private; + struct hl_device *hdev = entry->hdev; + u64 addr = entry->addr; + u32 value; + ssize_t rc; + + if (atomic_read(&hdev->in_reset)) { + dev_warn_ratelimited(hdev->dev, "Can't write during reset\n"); + return 0; + } + + rc = kstrtouint_from_user(buf, count, 16, &value); + if (rc) + return rc; + + if (hl_is_device_va(hdev, addr)) { + rc = device_va_to_pa(hdev, addr, &addr); + if (rc) + return rc; + } + + rc = hdev->asic_funcs->debugfs_write32(hdev, addr, value); + if (rc) { + dev_err(hdev->dev, "Failed to write 0x%08x to 0x%010llx\n", + value, addr); + return rc; + } + + return count; +} + +static ssize_t hl_data_read64(struct file *f, char __user *buf, + size_t count, loff_t *ppos) +{ + struct hl_dbg_device_entry *entry = file_inode(f)->i_private; + struct hl_device *hdev = entry->hdev; + char tmp_buf[32]; + u64 addr = entry->addr; + u64 val; + ssize_t rc; + + if (*ppos) + return 0; + + if (hl_is_device_va(hdev, addr)) { + rc = device_va_to_pa(hdev, addr, &addr); + if (rc) + return rc; + } + + rc = hdev->asic_funcs->debugfs_read64(hdev, addr, &val); + if (rc) { + dev_err(hdev->dev, "Failed to read from 0x%010llx\n", addr); + return rc; + } + + sprintf(tmp_buf, "0x%016llx\n", val); + return simple_read_from_buffer(buf, count, ppos, tmp_buf, + strlen(tmp_buf)); +} + +static ssize_t hl_data_write64(struct file *f, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct hl_dbg_device_entry *entry = file_inode(f)->i_private; + struct hl_device *hdev = entry->hdev; + u64 addr = entry->addr; + u64 value; + ssize_t rc; + + rc = kstrtoull_from_user(buf, count, 16, &value); + if (rc) + return rc; + + if (hl_is_device_va(hdev, addr)) { + rc = device_va_to_pa(hdev, addr, &addr); + if (rc) + return rc; + } + + rc = hdev->asic_funcs->debugfs_write64(hdev, addr, value); + if (rc) { + dev_err(hdev->dev, "Failed to write 0x%016llx to 0x%010llx\n", + value, addr); + return rc; + } + + return count; +} + +static ssize_t hl_get_power_state(struct file *f, char __user *buf, + size_t count, loff_t *ppos) +{ + struct hl_dbg_device_entry *entry = file_inode(f)->i_private; + struct hl_device *hdev = entry->hdev; + char tmp_buf[200]; + int i; + + if (*ppos) + return 0; + + if (hdev->pdev->current_state == PCI_D0) + i = 1; + else if (hdev->pdev->current_state == PCI_D3hot) + i = 2; + else + i = 3; + + sprintf(tmp_buf, + "current power state: %d\n1 - D0\n2 - D3hot\n3 - Unknown\n", i); + return simple_read_from_buffer(buf, count, ppos, tmp_buf, + strlen(tmp_buf)); +} + +static ssize_t hl_set_power_state(struct file *f, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct hl_dbg_device_entry *entry = file_inode(f)->i_private; + struct hl_device *hdev = entry->hdev; + u32 value; + ssize_t rc; + + rc = kstrtouint_from_user(buf, count, 10, &value); + if (rc) + return rc; + + if (value == 1) { + pci_set_power_state(hdev->pdev, PCI_D0); + pci_restore_state(hdev->pdev); + rc = pci_enable_device(hdev->pdev); + } else if (value == 2) { + pci_save_state(hdev->pdev); + pci_disable_device(hdev->pdev); + pci_set_power_state(hdev->pdev, PCI_D3hot); + } else { + dev_dbg(hdev->dev, "invalid power state value %u\n", value); + return -EINVAL; + } + + return count; +} + +static ssize_t hl_i2c_data_read(struct file *f, char __user *buf, + size_t count, loff_t *ppos) +{ + struct hl_dbg_device_entry *entry = file_inode(f)->i_private; + struct hl_device *hdev = entry->hdev; + char tmp_buf[32]; + u32 val; + ssize_t rc; + + if (*ppos) + return 0; + + rc = hl_debugfs_i2c_read(hdev, entry->i2c_bus, entry->i2c_addr, + entry->i2c_reg, &val); + if (rc) { + dev_err(hdev->dev, + "Failed to read from I2C bus %d, addr %d, reg %d\n", + entry->i2c_bus, entry->i2c_addr, entry->i2c_reg); + return rc; + } + + sprintf(tmp_buf, "0x%02x\n", val); + rc = simple_read_from_buffer(buf, count, ppos, tmp_buf, + strlen(tmp_buf)); + + return rc; +} + +static ssize_t hl_i2c_data_write(struct file *f, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct hl_dbg_device_entry *entry = file_inode(f)->i_private; + struct hl_device *hdev = entry->hdev; + u32 value; + ssize_t rc; + + rc = kstrtouint_from_user(buf, count, 16, &value); + if (rc) + return rc; + + rc = hl_debugfs_i2c_write(hdev, entry->i2c_bus, entry->i2c_addr, + entry->i2c_reg, value); + if (rc) { + dev_err(hdev->dev, + "Failed to write 0x%02x to I2C bus %d, addr %d, reg %d\n", + value, entry->i2c_bus, entry->i2c_addr, entry->i2c_reg); + return rc; + } + + return count; +} + +static ssize_t hl_led0_write(struct file *f, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct hl_dbg_device_entry *entry = file_inode(f)->i_private; + struct hl_device *hdev = entry->hdev; + u32 value; + ssize_t rc; + + rc = kstrtouint_from_user(buf, count, 10, &value); + if (rc) + return rc; + + value = value ? 1 : 0; + + hl_debugfs_led_set(hdev, 0, value); + + return count; +} + +static ssize_t hl_led1_write(struct file *f, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct hl_dbg_device_entry *entry = file_inode(f)->i_private; + struct hl_device *hdev = entry->hdev; + u32 value; + ssize_t rc; + + rc = kstrtouint_from_user(buf, count, 10, &value); + if (rc) + return rc; + + value = value ? 1 : 0; + + hl_debugfs_led_set(hdev, 1, value); + + return count; +} + +static ssize_t hl_led2_write(struct file *f, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct hl_dbg_device_entry *entry = file_inode(f)->i_private; + struct hl_device *hdev = entry->hdev; + u32 value; + ssize_t rc; + + rc = kstrtouint_from_user(buf, count, 10, &value); + if (rc) + return rc; + + value = value ? 1 : 0; + + hl_debugfs_led_set(hdev, 2, value); + + return count; +} + +static ssize_t hl_device_read(struct file *f, char __user *buf, + size_t count, loff_t *ppos) +{ + static const char *help = + "Valid values: disable, enable, suspend, resume, cpu_timeout\n"; + return simple_read_from_buffer(buf, count, ppos, help, strlen(help)); +} + +static ssize_t hl_device_write(struct file *f, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct hl_dbg_device_entry *entry = file_inode(f)->i_private; + struct hl_device *hdev = entry->hdev; + char data[30] = {0}; + + /* don't allow partial writes */ + if (*ppos != 0) + return 0; + + simple_write_to_buffer(data, 29, ppos, buf, count); + + if (strncmp("disable", data, strlen("disable")) == 0) { + hdev->disabled = true; + } else if (strncmp("enable", data, strlen("enable")) == 0) { + hdev->disabled = false; + } else if (strncmp("suspend", data, strlen("suspend")) == 0) { + hdev->asic_funcs->suspend(hdev); + } else if (strncmp("resume", data, strlen("resume")) == 0) { + hdev->asic_funcs->resume(hdev); + } else if (strncmp("cpu_timeout", data, strlen("cpu_timeout")) == 0) { + hdev->device_cpu_disabled = true; + } else { + dev_err(hdev->dev, + "Valid values: disable, enable, suspend, resume, cpu_timeout\n"); + count = -EINVAL; + } + + return count; +} + +static ssize_t hl_clk_gate_read(struct file *f, char __user *buf, + size_t count, loff_t *ppos) +{ + struct hl_dbg_device_entry *entry = file_inode(f)->i_private; + struct hl_device *hdev = entry->hdev; + char tmp_buf[200]; + ssize_t rc; + + if (*ppos) + return 0; + + sprintf(tmp_buf, "%d\n", hdev->clock_gating); + rc = simple_read_from_buffer(buf, strlen(tmp_buf) + 1, ppos, tmp_buf, + strlen(tmp_buf) + 1); + + return rc; +} + +static ssize_t hl_clk_gate_write(struct file *f, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct hl_dbg_device_entry *entry = file_inode(f)->i_private; + struct hl_device *hdev = entry->hdev; + u32 value; + ssize_t rc; + + if (atomic_read(&hdev->in_reset)) { + dev_warn_ratelimited(hdev->dev, + "Can't change clock gating during reset\n"); + return 0; + } + + rc = kstrtouint_from_user(buf, count, 10, &value); + if (rc) + return rc; + + if (value) { + hdev->clock_gating = 1; + if (hdev->asic_funcs->enable_clock_gating) + hdev->asic_funcs->enable_clock_gating(hdev); + } else { + if (hdev->asic_funcs->disable_clock_gating) + hdev->asic_funcs->disable_clock_gating(hdev); + hdev->clock_gating = 0; + } + + return count; +} + +static ssize_t hl_stop_on_err_read(struct file *f, char __user *buf, + size_t count, loff_t *ppos) +{ + struct hl_dbg_device_entry *entry = file_inode(f)->i_private; + struct hl_device *hdev = entry->hdev; + char tmp_buf[200]; + ssize_t rc; + + if (*ppos) + return 0; + + sprintf(tmp_buf, "%d\n", hdev->stop_on_err); + rc = simple_read_from_buffer(buf, strlen(tmp_buf) + 1, ppos, tmp_buf, + strlen(tmp_buf) + 1); + + return rc; +} + +static ssize_t hl_stop_on_err_write(struct file *f, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct hl_dbg_device_entry *entry = file_inode(f)->i_private; + struct hl_device *hdev = entry->hdev; + u32 value; + ssize_t rc; + + if (atomic_read(&hdev->in_reset)) { + dev_warn_ratelimited(hdev->dev, + "Can't change stop on error during reset\n"); + return 0; + } + + rc = kstrtouint_from_user(buf, count, 10, &value); + if (rc) + return rc; + + hdev->stop_on_err = value ? 1 : 0; + + hl_device_reset(hdev, false, false); + + return count; +} + +static const struct file_operations hl_data32b_fops = { + .owner = THIS_MODULE, + .read = hl_data_read32, + .write = hl_data_write32 +}; + +static const struct file_operations hl_data64b_fops = { + .owner = THIS_MODULE, + .read = hl_data_read64, + .write = hl_data_write64 +}; + +static const struct file_operations hl_i2c_data_fops = { + .owner = THIS_MODULE, + .read = hl_i2c_data_read, + .write = hl_i2c_data_write +}; + +static const struct file_operations hl_power_fops = { + .owner = THIS_MODULE, + .read = hl_get_power_state, + .write = hl_set_power_state +}; + +static const struct file_operations hl_led0_fops = { + .owner = THIS_MODULE, + .write = hl_led0_write +}; + +static const struct file_operations hl_led1_fops = { + .owner = THIS_MODULE, + .write = hl_led1_write +}; + +static const struct file_operations hl_led2_fops = { + .owner = THIS_MODULE, + .write = hl_led2_write +}; + +static const struct file_operations hl_device_fops = { + .owner = THIS_MODULE, + .read = hl_device_read, + .write = hl_device_write +}; + +static const struct file_operations hl_clk_gate_fops = { + .owner = THIS_MODULE, + .read = hl_clk_gate_read, + .write = hl_clk_gate_write +}; + +static const struct file_operations hl_stop_on_err_fops = { + .owner = THIS_MODULE, + .read = hl_stop_on_err_read, + .write = hl_stop_on_err_write +}; + +static const struct hl_info_list hl_debugfs_list[] = { + {"command_buffers", command_buffers_show, NULL}, + {"command_submission", command_submission_show, NULL}, + {"command_submission_jobs", command_submission_jobs_show, NULL}, + {"userptr", userptr_show, NULL}, + {"vm", vm_show, NULL}, + {"mmu", mmu_show, mmu_asid_va_write}, + {"engines", engines_show, NULL} +}; + +static int hl_debugfs_open(struct inode *inode, struct file *file) +{ + struct hl_debugfs_entry *node = inode->i_private; + + return single_open(file, node->info_ent->show, node); +} + +static ssize_t hl_debugfs_write(struct file *file, const char __user *buf, + size_t count, loff_t *f_pos) +{ + struct hl_debugfs_entry *node = file->f_inode->i_private; + + if (node->info_ent->write) + return node->info_ent->write(file, buf, count, f_pos); + else + return -EINVAL; + +} + +static const struct file_operations hl_debugfs_fops = { + .owner = THIS_MODULE, + .open = hl_debugfs_open, + .read = seq_read, + .write = hl_debugfs_write, + .llseek = seq_lseek, + .release = single_release, +}; + +void hl_debugfs_add_device(struct hl_device *hdev) +{ + struct hl_dbg_device_entry *dev_entry = &hdev->hl_debugfs; + int count = ARRAY_SIZE(hl_debugfs_list); + struct hl_debugfs_entry *entry; + struct dentry *ent; + int i; + + dev_entry->hdev = hdev; + dev_entry->entry_arr = kmalloc_array(count, + sizeof(struct hl_debugfs_entry), + GFP_KERNEL); + if (!dev_entry->entry_arr) + return; + + INIT_LIST_HEAD(&dev_entry->file_list); + INIT_LIST_HEAD(&dev_entry->cb_list); + INIT_LIST_HEAD(&dev_entry->cs_list); + INIT_LIST_HEAD(&dev_entry->cs_job_list); + INIT_LIST_HEAD(&dev_entry->userptr_list); + INIT_LIST_HEAD(&dev_entry->ctx_mem_hash_list); + mutex_init(&dev_entry->file_mutex); + spin_lock_init(&dev_entry->cb_spinlock); + spin_lock_init(&dev_entry->cs_spinlock); + spin_lock_init(&dev_entry->cs_job_spinlock); + spin_lock_init(&dev_entry->userptr_spinlock); + spin_lock_init(&dev_entry->ctx_mem_hash_spinlock); + + dev_entry->root = debugfs_create_dir(dev_name(hdev->dev), + hl_debug_root); + + debugfs_create_x64("addr", + 0644, + dev_entry->root, + &dev_entry->addr); + + debugfs_create_file("data32", + 0644, + dev_entry->root, + dev_entry, + &hl_data32b_fops); + + debugfs_create_file("data64", + 0644, + dev_entry->root, + dev_entry, + &hl_data64b_fops); + + debugfs_create_file("set_power_state", + 0200, + dev_entry->root, + dev_entry, + &hl_power_fops); + + debugfs_create_u8("i2c_bus", + 0644, + dev_entry->root, + &dev_entry->i2c_bus); + + debugfs_create_u8("i2c_addr", + 0644, + dev_entry->root, + &dev_entry->i2c_addr); + + debugfs_create_u8("i2c_reg", + 0644, + dev_entry->root, + &dev_entry->i2c_reg); + + debugfs_create_file("i2c_data", + 0644, + dev_entry->root, + dev_entry, + &hl_i2c_data_fops); + + debugfs_create_file("led0", + 0200, + dev_entry->root, + dev_entry, + &hl_led0_fops); + + debugfs_create_file("led1", + 0200, + dev_entry->root, + dev_entry, + &hl_led1_fops); + + debugfs_create_file("led2", + 0200, + dev_entry->root, + dev_entry, + &hl_led2_fops); + + debugfs_create_file("device", + 0200, + dev_entry->root, + dev_entry, + &hl_device_fops); + + debugfs_create_file("clk_gate", + 0200, + dev_entry->root, + dev_entry, + &hl_clk_gate_fops); + + debugfs_create_file("stop_on_err", + 0644, + dev_entry->root, + dev_entry, + &hl_stop_on_err_fops); + + for (i = 0, entry = dev_entry->entry_arr ; i < count ; i++, entry++) { + + ent = debugfs_create_file(hl_debugfs_list[i].name, + 0444, + dev_entry->root, + entry, + &hl_debugfs_fops); + entry->dent = ent; + entry->info_ent = &hl_debugfs_list[i]; + entry->dev_entry = dev_entry; + } +} + +void hl_debugfs_remove_device(struct hl_device *hdev) +{ + struct hl_dbg_device_entry *entry = &hdev->hl_debugfs; + + debugfs_remove_recursive(entry->root); + + mutex_destroy(&entry->file_mutex); + kfree(entry->entry_arr); +} + +void hl_debugfs_add_file(struct hl_fpriv *hpriv) +{ + struct hl_dbg_device_entry *dev_entry = &hpriv->hdev->hl_debugfs; + + mutex_lock(&dev_entry->file_mutex); + list_add(&hpriv->debugfs_list, &dev_entry->file_list); + mutex_unlock(&dev_entry->file_mutex); +} + +void hl_debugfs_remove_file(struct hl_fpriv *hpriv) +{ + struct hl_dbg_device_entry *dev_entry = &hpriv->hdev->hl_debugfs; + + mutex_lock(&dev_entry->file_mutex); + list_del(&hpriv->debugfs_list); + mutex_unlock(&dev_entry->file_mutex); +} + +void hl_debugfs_add_cb(struct hl_cb *cb) +{ + struct hl_dbg_device_entry *dev_entry = &cb->hdev->hl_debugfs; + + spin_lock(&dev_entry->cb_spinlock); + list_add(&cb->debugfs_list, &dev_entry->cb_list); + spin_unlock(&dev_entry->cb_spinlock); +} + +void hl_debugfs_remove_cb(struct hl_cb *cb) +{ + struct hl_dbg_device_entry *dev_entry = &cb->hdev->hl_debugfs; + + spin_lock(&dev_entry->cb_spinlock); + list_del(&cb->debugfs_list); + spin_unlock(&dev_entry->cb_spinlock); +} + +void hl_debugfs_add_cs(struct hl_cs *cs) +{ + struct hl_dbg_device_entry *dev_entry = &cs->ctx->hdev->hl_debugfs; + + spin_lock(&dev_entry->cs_spinlock); + list_add(&cs->debugfs_list, &dev_entry->cs_list); + spin_unlock(&dev_entry->cs_spinlock); +} + +void hl_debugfs_remove_cs(struct hl_cs *cs) +{ + struct hl_dbg_device_entry *dev_entry = &cs->ctx->hdev->hl_debugfs; + + spin_lock(&dev_entry->cs_spinlock); + list_del(&cs->debugfs_list); + spin_unlock(&dev_entry->cs_spinlock); +} + +void hl_debugfs_add_job(struct hl_device *hdev, struct hl_cs_job *job) +{ + struct hl_dbg_device_entry *dev_entry = &hdev->hl_debugfs; + + spin_lock(&dev_entry->cs_job_spinlock); + list_add(&job->debugfs_list, &dev_entry->cs_job_list); + spin_unlock(&dev_entry->cs_job_spinlock); +} + +void hl_debugfs_remove_job(struct hl_device *hdev, struct hl_cs_job *job) +{ + struct hl_dbg_device_entry *dev_entry = &hdev->hl_debugfs; + + spin_lock(&dev_entry->cs_job_spinlock); + list_del(&job->debugfs_list); + spin_unlock(&dev_entry->cs_job_spinlock); +} + +void hl_debugfs_add_userptr(struct hl_device *hdev, struct hl_userptr *userptr) +{ + struct hl_dbg_device_entry *dev_entry = &hdev->hl_debugfs; + + spin_lock(&dev_entry->userptr_spinlock); + list_add(&userptr->debugfs_list, &dev_entry->userptr_list); + spin_unlock(&dev_entry->userptr_spinlock); +} + +void hl_debugfs_remove_userptr(struct hl_device *hdev, + struct hl_userptr *userptr) +{ + struct hl_dbg_device_entry *dev_entry = &hdev->hl_debugfs; + + spin_lock(&dev_entry->userptr_spinlock); + list_del(&userptr->debugfs_list); + spin_unlock(&dev_entry->userptr_spinlock); +} + +void hl_debugfs_add_ctx_mem_hash(struct hl_device *hdev, struct hl_ctx *ctx) +{ + struct hl_dbg_device_entry *dev_entry = &hdev->hl_debugfs; + + spin_lock(&dev_entry->ctx_mem_hash_spinlock); + list_add(&ctx->debugfs_list, &dev_entry->ctx_mem_hash_list); + spin_unlock(&dev_entry->ctx_mem_hash_spinlock); +} + +void hl_debugfs_remove_ctx_mem_hash(struct hl_device *hdev, struct hl_ctx *ctx) +{ + struct hl_dbg_device_entry *dev_entry = &hdev->hl_debugfs; + + spin_lock(&dev_entry->ctx_mem_hash_spinlock); + list_del(&ctx->debugfs_list); + spin_unlock(&dev_entry->ctx_mem_hash_spinlock); +} + +void __init hl_debugfs_init(void) +{ + hl_debug_root = debugfs_create_dir("habanalabs", NULL); +} + +void hl_debugfs_fini(void) +{ + debugfs_remove_recursive(hl_debug_root); +} diff --git a/drivers/misc/habanalabs/common/device.c b/drivers/misc/habanalabs/common/device.c new file mode 100644 index 000000000000..84800efec10d --- /dev/null +++ b/drivers/misc/habanalabs/common/device.c @@ -0,0 +1,1506 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright 2016-2019 HabanaLabs, Ltd. + * All Rights Reserved. + */ + +#define pr_fmt(fmt) "habanalabs: " fmt + +#include "habanalabs.h" + +#include +#include +#include +#include + +#define HL_PLDM_PENDING_RESET_PER_SEC (HL_PENDING_RESET_PER_SEC * 10) + +bool hl_device_disabled_or_in_reset(struct hl_device *hdev) +{ + if ((hdev->disabled) || (atomic_read(&hdev->in_reset))) + return true; + else + return false; +} + +enum hl_device_status hl_device_status(struct hl_device *hdev) +{ + enum hl_device_status status; + + if (hdev->disabled) + status = HL_DEVICE_STATUS_MALFUNCTION; + else if (atomic_read(&hdev->in_reset)) + status = HL_DEVICE_STATUS_IN_RESET; + else + status = HL_DEVICE_STATUS_OPERATIONAL; + + return status; +} + +static void hpriv_release(struct kref *ref) +{ + struct hl_fpriv *hpriv; + struct hl_device *hdev; + + hpriv = container_of(ref, struct hl_fpriv, refcount); + + hdev = hpriv->hdev; + + put_pid(hpriv->taskpid); + + hl_debugfs_remove_file(hpriv); + + mutex_destroy(&hpriv->restore_phase_mutex); + + mutex_lock(&hdev->fpriv_list_lock); + list_del(&hpriv->dev_node); + hdev->compute_ctx = NULL; + mutex_unlock(&hdev->fpriv_list_lock); + + kfree(hpriv); +} + +void hl_hpriv_get(struct hl_fpriv *hpriv) +{ + kref_get(&hpriv->refcount); +} + +void hl_hpriv_put(struct hl_fpriv *hpriv) +{ + kref_put(&hpriv->refcount, hpriv_release); +} + +/* + * hl_device_release - release function for habanalabs device + * + * @inode: pointer to inode structure + * @filp: pointer to file structure + * + * Called when process closes an habanalabs device + */ +static int hl_device_release(struct inode *inode, struct file *filp) +{ + struct hl_fpriv *hpriv = filp->private_data; + + hl_cb_mgr_fini(hpriv->hdev, &hpriv->cb_mgr); + hl_ctx_mgr_fini(hpriv->hdev, &hpriv->ctx_mgr); + + filp->private_data = NULL; + + hl_hpriv_put(hpriv); + + return 0; +} + +static int hl_device_release_ctrl(struct inode *inode, struct file *filp) +{ + struct hl_fpriv *hpriv = filp->private_data; + struct hl_device *hdev; + + filp->private_data = NULL; + + hdev = hpriv->hdev; + + mutex_lock(&hdev->fpriv_list_lock); + list_del(&hpriv->dev_node); + mutex_unlock(&hdev->fpriv_list_lock); + + kfree(hpriv); + + return 0; +} + +/* + * hl_mmap - mmap function for habanalabs device + * + * @*filp: pointer to file structure + * @*vma: pointer to vm_area_struct of the process + * + * Called when process does an mmap on habanalabs device. Call the device's mmap + * function at the end of the common code. + */ +static int hl_mmap(struct file *filp, struct vm_area_struct *vma) +{ + struct hl_fpriv *hpriv = filp->private_data; + + if ((vma->vm_pgoff & HL_MMAP_CB_MASK) == HL_MMAP_CB_MASK) { + vma->vm_pgoff ^= HL_MMAP_CB_MASK; + return hl_cb_mmap(hpriv, vma); + } + + return -EINVAL; +} + +static const struct file_operations hl_ops = { + .owner = THIS_MODULE, + .open = hl_device_open, + .release = hl_device_release, + .mmap = hl_mmap, + .unlocked_ioctl = hl_ioctl, + .compat_ioctl = hl_ioctl +}; + +static const struct file_operations hl_ctrl_ops = { + .owner = THIS_MODULE, + .open = hl_device_open_ctrl, + .release = hl_device_release_ctrl, + .unlocked_ioctl = hl_ioctl_control, + .compat_ioctl = hl_ioctl_control +}; + +static void device_release_func(struct device *dev) +{ + kfree(dev); +} + +/* + * device_init_cdev - Initialize cdev and device for habanalabs device + * + * @hdev: pointer to habanalabs device structure + * @hclass: pointer to the class object of the device + * @minor: minor number of the specific device + * @fpos: file operations to install for this device + * @name: name of the device as it will appear in the filesystem + * @cdev: pointer to the char device object that will be initialized + * @dev: pointer to the device object that will be initialized + * + * Initialize a cdev and a Linux device for habanalabs's device. + */ +static int device_init_cdev(struct hl_device *hdev, struct class *hclass, + int minor, const struct file_operations *fops, + char *name, struct cdev *cdev, + struct device **dev) +{ + cdev_init(cdev, fops); + cdev->owner = THIS_MODULE; + + *dev = kzalloc(sizeof(**dev), GFP_KERNEL); + if (!*dev) + return -ENOMEM; + + device_initialize(*dev); + (*dev)->devt = MKDEV(hdev->major, minor); + (*dev)->class = hclass; + (*dev)->release = device_release_func; + dev_set_drvdata(*dev, hdev); + dev_set_name(*dev, "%s", name); + + return 0; +} + +static int device_cdev_sysfs_add(struct hl_device *hdev) +{ + int rc; + + rc = cdev_device_add(&hdev->cdev, hdev->dev); + if (rc) { + dev_err(hdev->dev, + "failed to add a char device to the system\n"); + return rc; + } + + rc = cdev_device_add(&hdev->cdev_ctrl, hdev->dev_ctrl); + if (rc) { + dev_err(hdev->dev, + "failed to add a control char device to the system\n"); + goto delete_cdev_device; + } + + /* hl_sysfs_init() must be done after adding the device to the system */ + rc = hl_sysfs_init(hdev); + if (rc) { + dev_err(hdev->dev, "failed to initialize sysfs\n"); + goto delete_ctrl_cdev_device; + } + + hdev->cdev_sysfs_created = true; + + return 0; + +delete_ctrl_cdev_device: + cdev_device_del(&hdev->cdev_ctrl, hdev->dev_ctrl); +delete_cdev_device: + cdev_device_del(&hdev->cdev, hdev->dev); + return rc; +} + +static void device_cdev_sysfs_del(struct hl_device *hdev) +{ + /* device_release() won't be called so must free devices explicitly */ + if (!hdev->cdev_sysfs_created) { + kfree(hdev->dev_ctrl); + kfree(hdev->dev); + return; + } + + hl_sysfs_fini(hdev); + cdev_device_del(&hdev->cdev_ctrl, hdev->dev_ctrl); + cdev_device_del(&hdev->cdev, hdev->dev); +} + +/* + * device_early_init - do some early initialization for the habanalabs device + * + * @hdev: pointer to habanalabs device structure + * + * Install the relevant function pointers and call the early_init function, + * if such a function exists + */ +static int device_early_init(struct hl_device *hdev) +{ + int i, rc; + char workq_name[32]; + + switch (hdev->asic_type) { + case ASIC_GOYA: + goya_set_asic_funcs(hdev); + strlcpy(hdev->asic_name, "GOYA", sizeof(hdev->asic_name)); + break; + case ASIC_GAUDI: + gaudi_set_asic_funcs(hdev); + sprintf(hdev->asic_name, "GAUDI"); + break; + default: + dev_err(hdev->dev, "Unrecognized ASIC type %d\n", + hdev->asic_type); + return -EINVAL; + } + + rc = hdev->asic_funcs->early_init(hdev); + if (rc) + return rc; + + rc = hl_asid_init(hdev); + if (rc) + goto early_fini; + + if (hdev->asic_prop.completion_queues_count) { + hdev->cq_wq = kcalloc(hdev->asic_prop.completion_queues_count, + sizeof(*hdev->cq_wq), + GFP_ATOMIC); + if (!hdev->cq_wq) { + rc = -ENOMEM; + goto asid_fini; + } + } + + for (i = 0 ; i < hdev->asic_prop.completion_queues_count ; i++) { + snprintf(workq_name, 32, "hl-free-jobs-%u", i); + hdev->cq_wq[i] = create_singlethread_workqueue(workq_name); + if (hdev->cq_wq == NULL) { + dev_err(hdev->dev, "Failed to allocate CQ workqueue\n"); + rc = -ENOMEM; + goto free_cq_wq; + } + } + + hdev->eq_wq = alloc_workqueue("hl-events", WQ_UNBOUND, 0); + if (hdev->eq_wq == NULL) { + dev_err(hdev->dev, "Failed to allocate EQ workqueue\n"); + rc = -ENOMEM; + goto free_cq_wq; + } + + hdev->hl_chip_info = kzalloc(sizeof(struct hwmon_chip_info), + GFP_KERNEL); + if (!hdev->hl_chip_info) { + rc = -ENOMEM; + goto free_eq_wq; + } + + hdev->idle_busy_ts_arr = kmalloc_array(HL_IDLE_BUSY_TS_ARR_SIZE, + sizeof(struct hl_device_idle_busy_ts), + (GFP_KERNEL | __GFP_ZERO)); + if (!hdev->idle_busy_ts_arr) { + rc = -ENOMEM; + goto free_chip_info; + } + + hl_cb_mgr_init(&hdev->kernel_cb_mgr); + + mutex_init(&hdev->send_cpu_message_lock); + mutex_init(&hdev->debug_lock); + mutex_init(&hdev->mmu_cache_lock); + INIT_LIST_HEAD(&hdev->hw_queues_mirror_list); + spin_lock_init(&hdev->hw_queues_mirror_lock); + INIT_LIST_HEAD(&hdev->fpriv_list); + mutex_init(&hdev->fpriv_list_lock); + atomic_set(&hdev->in_reset, 0); + + return 0; + +free_chip_info: + kfree(hdev->hl_chip_info); +free_eq_wq: + destroy_workqueue(hdev->eq_wq); +free_cq_wq: + for (i = 0 ; i < hdev->asic_prop.completion_queues_count ; i++) + if (hdev->cq_wq[i]) + destroy_workqueue(hdev->cq_wq[i]); + kfree(hdev->cq_wq); +asid_fini: + hl_asid_fini(hdev); +early_fini: + if (hdev->asic_funcs->early_fini) + hdev->asic_funcs->early_fini(hdev); + + return rc; +} + +/* + * device_early_fini - finalize all that was done in device_early_init + * + * @hdev: pointer to habanalabs device structure + * + */ +static void device_early_fini(struct hl_device *hdev) +{ + int i; + + mutex_destroy(&hdev->mmu_cache_lock); + mutex_destroy(&hdev->debug_lock); + mutex_destroy(&hdev->send_cpu_message_lock); + + mutex_destroy(&hdev->fpriv_list_lock); + + hl_cb_mgr_fini(hdev, &hdev->kernel_cb_mgr); + + kfree(hdev->idle_busy_ts_arr); + kfree(hdev->hl_chip_info); + + destroy_workqueue(hdev->eq_wq); + + for (i = 0 ; i < hdev->asic_prop.completion_queues_count ; i++) + destroy_workqueue(hdev->cq_wq[i]); + kfree(hdev->cq_wq); + + hl_asid_fini(hdev); + + if (hdev->asic_funcs->early_fini) + hdev->asic_funcs->early_fini(hdev); +} + +static void set_freq_to_low_job(struct work_struct *work) +{ + struct hl_device *hdev = container_of(work, struct hl_device, + work_freq.work); + + mutex_lock(&hdev->fpriv_list_lock); + + if (!hdev->compute_ctx) + hl_device_set_frequency(hdev, PLL_LOW); + + mutex_unlock(&hdev->fpriv_list_lock); + + schedule_delayed_work(&hdev->work_freq, + usecs_to_jiffies(HL_PLL_LOW_JOB_FREQ_USEC)); +} + +static void hl_device_heartbeat(struct work_struct *work) +{ + struct hl_device *hdev = container_of(work, struct hl_device, + work_heartbeat.work); + + if (hl_device_disabled_or_in_reset(hdev)) + goto reschedule; + + if (!hdev->asic_funcs->send_heartbeat(hdev)) + goto reschedule; + + dev_err(hdev->dev, "Device heartbeat failed!\n"); + hl_device_reset(hdev, true, false); + + return; + +reschedule: + schedule_delayed_work(&hdev->work_heartbeat, + usecs_to_jiffies(HL_HEARTBEAT_PER_USEC)); +} + +/* + * device_late_init - do late stuff initialization for the habanalabs device + * + * @hdev: pointer to habanalabs device structure + * + * Do stuff that either needs the device H/W queues to be active or needs + * to happen after all the rest of the initialization is finished + */ +static int device_late_init(struct hl_device *hdev) +{ + int rc; + + if (hdev->asic_funcs->late_init) { + rc = hdev->asic_funcs->late_init(hdev); + if (rc) { + dev_err(hdev->dev, + "failed late initialization for the H/W\n"); + return rc; + } + } + + hdev->high_pll = hdev->asic_prop.high_pll; + + /* force setting to low frequency */ + hdev->curr_pll_profile = PLL_LOW; + + if (hdev->pm_mng_profile == PM_AUTO) + hdev->asic_funcs->set_pll_profile(hdev, PLL_LOW); + else + hdev->asic_funcs->set_pll_profile(hdev, PLL_LAST); + + INIT_DELAYED_WORK(&hdev->work_freq, set_freq_to_low_job); + schedule_delayed_work(&hdev->work_freq, + usecs_to_jiffies(HL_PLL_LOW_JOB_FREQ_USEC)); + + if (hdev->heartbeat) { + INIT_DELAYED_WORK(&hdev->work_heartbeat, hl_device_heartbeat); + schedule_delayed_work(&hdev->work_heartbeat, + usecs_to_jiffies(HL_HEARTBEAT_PER_USEC)); + } + + hdev->late_init_done = true; + + return 0; +} + +/* + * device_late_fini - finalize all that was done in device_late_init + * + * @hdev: pointer to habanalabs device structure + * + */ +static void device_late_fini(struct hl_device *hdev) +{ + if (!hdev->late_init_done) + return; + + cancel_delayed_work_sync(&hdev->work_freq); + if (hdev->heartbeat) + cancel_delayed_work_sync(&hdev->work_heartbeat); + + if (hdev->asic_funcs->late_fini) + hdev->asic_funcs->late_fini(hdev); + + hdev->late_init_done = false; +} + +uint32_t hl_device_utilization(struct hl_device *hdev, uint32_t period_ms) +{ + struct hl_device_idle_busy_ts *ts; + ktime_t zero_ktime, curr = ktime_get(); + u32 overlap_cnt = 0, last_index = hdev->idle_busy_ts_idx; + s64 period_us, last_start_us, last_end_us, last_busy_time_us, + total_busy_time_us = 0, total_busy_time_ms; + + zero_ktime = ktime_set(0, 0); + period_us = period_ms * USEC_PER_MSEC; + ts = &hdev->idle_busy_ts_arr[last_index]; + + /* check case that device is currently in idle */ + if (!ktime_compare(ts->busy_to_idle_ts, zero_ktime) && + !ktime_compare(ts->idle_to_busy_ts, zero_ktime)) { + + last_index--; + /* Handle case idle_busy_ts_idx was 0 */ + if (last_index > HL_IDLE_BUSY_TS_ARR_SIZE) + last_index = HL_IDLE_BUSY_TS_ARR_SIZE - 1; + + ts = &hdev->idle_busy_ts_arr[last_index]; + } + + while (overlap_cnt < HL_IDLE_BUSY_TS_ARR_SIZE) { + /* Check if we are in last sample case. i.e. if the sample + * begun before the sampling period. This could be a real + * sample or 0 so need to handle both cases + */ + last_start_us = ktime_to_us( + ktime_sub(curr, ts->idle_to_busy_ts)); + + if (last_start_us > period_us) { + + /* First check two cases: + * 1. If the device is currently busy + * 2. If the device was idle during the whole sampling + * period + */ + + if (!ktime_compare(ts->busy_to_idle_ts, zero_ktime)) { + /* Check if the device is currently busy */ + if (ktime_compare(ts->idle_to_busy_ts, + zero_ktime)) + return 100; + + /* We either didn't have any activity or we + * reached an entry which is 0. Either way, + * exit and return what was accumulated so far + */ + break; + } + + /* If sample has finished, check it is relevant */ + last_end_us = ktime_to_us( + ktime_sub(curr, ts->busy_to_idle_ts)); + + if (last_end_us > period_us) + break; + + /* It is relevant so add it but with adjustment */ + last_busy_time_us = ktime_to_us( + ktime_sub(ts->busy_to_idle_ts, + ts->idle_to_busy_ts)); + total_busy_time_us += last_busy_time_us - + (last_start_us - period_us); + break; + } + + /* Check if the sample is finished or still open */ + if (ktime_compare(ts->busy_to_idle_ts, zero_ktime)) + last_busy_time_us = ktime_to_us( + ktime_sub(ts->busy_to_idle_ts, + ts->idle_to_busy_ts)); + else + last_busy_time_us = ktime_to_us( + ktime_sub(curr, ts->idle_to_busy_ts)); + + total_busy_time_us += last_busy_time_us; + + last_index--; + /* Handle case idle_busy_ts_idx was 0 */ + if (last_index > HL_IDLE_BUSY_TS_ARR_SIZE) + last_index = HL_IDLE_BUSY_TS_ARR_SIZE - 1; + + ts = &hdev->idle_busy_ts_arr[last_index]; + + overlap_cnt++; + } + + total_busy_time_ms = DIV_ROUND_UP_ULL(total_busy_time_us, + USEC_PER_MSEC); + + return DIV_ROUND_UP_ULL(total_busy_time_ms * 100, period_ms); +} + +/* + * hl_device_set_frequency - set the frequency of the device + * + * @hdev: pointer to habanalabs device structure + * @freq: the new frequency value + * + * Change the frequency if needed. This function has no protection against + * concurrency, therefore it is assumed that the calling function has protected + * itself against the case of calling this function from multiple threads with + * different values + * + * Returns 0 if no change was done, otherwise returns 1 + */ +int hl_device_set_frequency(struct hl_device *hdev, enum hl_pll_frequency freq) +{ + if ((hdev->pm_mng_profile == PM_MANUAL) || + (hdev->curr_pll_profile == freq)) + return 0; + + dev_dbg(hdev->dev, "Changing device frequency to %s\n", + freq == PLL_HIGH ? "high" : "low"); + + hdev->asic_funcs->set_pll_profile(hdev, freq); + + hdev->curr_pll_profile = freq; + + return 1; +} + +int hl_device_set_debug_mode(struct hl_device *hdev, bool enable) +{ + int rc = 0; + + mutex_lock(&hdev->debug_lock); + + if (!enable) { + if (!hdev->in_debug) { + dev_err(hdev->dev, + "Failed to disable debug mode because device was not in debug mode\n"); + rc = -EFAULT; + goto out; + } + + if (!hdev->hard_reset_pending) + hdev->asic_funcs->halt_coresight(hdev); + + hdev->in_debug = 0; + + if (!hdev->hard_reset_pending) + hdev->asic_funcs->enable_clock_gating(hdev); + + goto out; + } + + if (hdev->in_debug) { + dev_err(hdev->dev, + "Failed to enable debug mode because device is already in debug mode\n"); + rc = -EFAULT; + goto out; + } + + hdev->asic_funcs->disable_clock_gating(hdev); + hdev->in_debug = 1; + +out: + mutex_unlock(&hdev->debug_lock); + + return rc; +} + +/* + * hl_device_suspend - initiate device suspend + * + * @hdev: pointer to habanalabs device structure + * + * Puts the hw in the suspend state (all asics). + * Returns 0 for success or an error on failure. + * Called at driver suspend. + */ +int hl_device_suspend(struct hl_device *hdev) +{ + int rc; + + pci_save_state(hdev->pdev); + + /* Block future CS/VM/JOB completion operations */ + rc = atomic_cmpxchg(&hdev->in_reset, 0, 1); + if (rc) { + dev_err(hdev->dev, "Can't suspend while in reset\n"); + return -EIO; + } + + /* This blocks all other stuff that is not blocked by in_reset */ + hdev->disabled = true; + + /* + * Flush anyone that is inside the critical section of enqueue + * jobs to the H/W + */ + hdev->asic_funcs->hw_queues_lock(hdev); + hdev->asic_funcs->hw_queues_unlock(hdev); + + /* Flush processes that are sending message to CPU */ + mutex_lock(&hdev->send_cpu_message_lock); + mutex_unlock(&hdev->send_cpu_message_lock); + + rc = hdev->asic_funcs->suspend(hdev); + if (rc) + dev_err(hdev->dev, + "Failed to disable PCI access of device CPU\n"); + + /* Shut down the device */ + pci_disable_device(hdev->pdev); + pci_set_power_state(hdev->pdev, PCI_D3hot); + + return 0; +} + +/* + * hl_device_resume - initiate device resume + * + * @hdev: pointer to habanalabs device structure + * + * Bring the hw back to operating state (all asics). + * Returns 0 for success or an error on failure. + * Called at driver resume. + */ +int hl_device_resume(struct hl_device *hdev) +{ + int rc; + + pci_set_power_state(hdev->pdev, PCI_D0); + pci_restore_state(hdev->pdev); + rc = pci_enable_device_mem(hdev->pdev); + if (rc) { + dev_err(hdev->dev, + "Failed to enable PCI device in resume\n"); + return rc; + } + + pci_set_master(hdev->pdev); + + rc = hdev->asic_funcs->resume(hdev); + if (rc) { + dev_err(hdev->dev, "Failed to resume device after suspend\n"); + goto disable_device; + } + + + hdev->disabled = false; + atomic_set(&hdev->in_reset, 0); + + rc = hl_device_reset(hdev, true, false); + if (rc) { + dev_err(hdev->dev, "Failed to reset device during resume\n"); + goto disable_device; + } + + return 0; + +disable_device: + pci_clear_master(hdev->pdev); + pci_disable_device(hdev->pdev); + + return rc; +} + +static int device_kill_open_processes(struct hl_device *hdev) +{ + u16 pending_total, pending_cnt; + struct hl_fpriv *hpriv; + struct task_struct *task = NULL; + + if (hdev->pldm) + pending_total = HL_PLDM_PENDING_RESET_PER_SEC; + else + pending_total = HL_PENDING_RESET_PER_SEC; + + /* Giving time for user to close FD, and for processes that are inside + * hl_device_open to finish + */ + if (!list_empty(&hdev->fpriv_list)) + ssleep(1); + + mutex_lock(&hdev->fpriv_list_lock); + + /* This section must be protected because we are dereferencing + * pointers that are freed if the process exits + */ + list_for_each_entry(hpriv, &hdev->fpriv_list, dev_node) { + task = get_pid_task(hpriv->taskpid, PIDTYPE_PID); + if (task) { + dev_info(hdev->dev, "Killing user process pid=%d\n", + task_pid_nr(task)); + send_sig(SIGKILL, task, 1); + usleep_range(1000, 10000); + + put_task_struct(task); + } + } + + mutex_unlock(&hdev->fpriv_list_lock); + + /* We killed the open users, but because the driver cleans up after the + * user contexts are closed (e.g. mmu mappings), we need to wait again + * to make sure the cleaning phase is finished before continuing with + * the reset + */ + + pending_cnt = pending_total; + + while ((!list_empty(&hdev->fpriv_list)) && (pending_cnt)) { + dev_info(hdev->dev, + "Waiting for all unmap operations to finish before hard reset\n"); + + pending_cnt--; + + ssleep(1); + } + + return list_empty(&hdev->fpriv_list) ? 0 : -EBUSY; +} + +static void device_hard_reset_pending(struct work_struct *work) +{ + struct hl_device_reset_work *device_reset_work = + container_of(work, struct hl_device_reset_work, reset_work); + struct hl_device *hdev = device_reset_work->hdev; + + hl_device_reset(hdev, true, true); + + kfree(device_reset_work); +} + +/* + * hl_device_reset - reset the device + * + * @hdev: pointer to habanalabs device structure + * @hard_reset: should we do hard reset to all engines or just reset the + * compute/dma engines + * @from_hard_reset_thread: is the caller the hard-reset thread + * + * Block future CS and wait for pending CS to be enqueued + * Call ASIC H/W fini + * Flush all completions + * Re-initialize all internal data structures + * Call ASIC H/W init, late_init + * Test queues + * Enable device + * + * Returns 0 for success or an error on failure. + */ +int hl_device_reset(struct hl_device *hdev, bool hard_reset, + bool from_hard_reset_thread) +{ + int i, rc; + + if (!hdev->init_done) { + dev_err(hdev->dev, + "Can't reset before initialization is done\n"); + return 0; + } + + if ((!hard_reset) && (!hdev->supports_soft_reset)) { + dev_dbg(hdev->dev, "Doing hard-reset instead of soft-reset\n"); + hard_reset = true; + } + + /* + * Prevent concurrency in this function - only one reset should be + * done at any given time. Only need to perform this if we didn't + * get from the dedicated hard reset thread + */ + if (!from_hard_reset_thread) { + /* Block future CS/VM/JOB completion operations */ + rc = atomic_cmpxchg(&hdev->in_reset, 0, 1); + if (rc) + return 0; + + if (hard_reset) { + /* Disable PCI access from device F/W so he won't send + * us additional interrupts. We disable MSI/MSI-X at + * the halt_engines function and we can't have the F/W + * sending us interrupts after that. We need to disable + * the access here because if the device is marked + * disable, the message won't be send. Also, in case + * of heartbeat, the device CPU is marked as disable + * so this message won't be sent + */ + if (hl_fw_send_pci_access_msg(hdev, + ARMCP_PACKET_DISABLE_PCI_ACCESS)) + dev_warn(hdev->dev, + "Failed to disable PCI access by F/W\n"); + } + + /* This also blocks future CS/VM/JOB completion operations */ + hdev->disabled = true; + + /* Flush anyone that is inside the critical section of enqueue + * jobs to the H/W + */ + hdev->asic_funcs->hw_queues_lock(hdev); + hdev->asic_funcs->hw_queues_unlock(hdev); + + /* Flush anyone that is inside device open */ + mutex_lock(&hdev->fpriv_list_lock); + mutex_unlock(&hdev->fpriv_list_lock); + + dev_err(hdev->dev, "Going to RESET device!\n"); + } + +again: + if ((hard_reset) && (!from_hard_reset_thread)) { + struct hl_device_reset_work *device_reset_work; + + hdev->hard_reset_pending = true; + + device_reset_work = kzalloc(sizeof(*device_reset_work), + GFP_ATOMIC); + if (!device_reset_work) { + rc = -ENOMEM; + goto out_err; + } + + /* + * Because the reset function can't run from interrupt or + * from heartbeat work, we need to call the reset function + * from a dedicated work + */ + INIT_WORK(&device_reset_work->reset_work, + device_hard_reset_pending); + device_reset_work->hdev = hdev; + schedule_work(&device_reset_work->reset_work); + + return 0; + } + + if (hard_reset) { + device_late_fini(hdev); + + /* + * Now that the heartbeat thread is closed, flush processes + * which are sending messages to CPU + */ + mutex_lock(&hdev->send_cpu_message_lock); + mutex_unlock(&hdev->send_cpu_message_lock); + } + + /* + * Halt the engines and disable interrupts so we won't get any more + * completions from H/W and we won't have any accesses from the + * H/W to the host machine + */ + hdev->asic_funcs->halt_engines(hdev, hard_reset); + + /* Go over all the queues, release all CS and their jobs */ + hl_cs_rollback_all(hdev); + + if (hard_reset) { + /* Kill processes here after CS rollback. This is because the + * process can't really exit until all its CSs are done, which + * is what we do in cs rollback + */ + rc = device_kill_open_processes(hdev); + if (rc) { + dev_crit(hdev->dev, + "Failed to kill all open processes, stopping hard reset\n"); + goto out_err; + } + + /* Flush the Event queue workers to make sure no other thread is + * reading or writing to registers during the reset + */ + flush_workqueue(hdev->eq_wq); + } + + /* Release kernel context */ + if ((hard_reset) && (hl_ctx_put(hdev->kernel_ctx) == 1)) + hdev->kernel_ctx = NULL; + + /* Reset the H/W. It will be in idle state after this returns */ + hdev->asic_funcs->hw_fini(hdev, hard_reset); + + if (hard_reset) { + hl_vm_fini(hdev); + hl_mmu_fini(hdev); + hl_eq_reset(hdev, &hdev->event_queue); + } + + /* Re-initialize PI,CI to 0 in all queues (hw queue, cq) */ + hl_hw_queue_reset(hdev, hard_reset); + for (i = 0 ; i < hdev->asic_prop.completion_queues_count ; i++) + hl_cq_reset(hdev, &hdev->completion_queue[i]); + + hdev->idle_busy_ts_idx = 0; + hdev->idle_busy_ts_arr[0].busy_to_idle_ts = ktime_set(0, 0); + hdev->idle_busy_ts_arr[0].idle_to_busy_ts = ktime_set(0, 0); + + if (hdev->cs_active_cnt) + dev_crit(hdev->dev, "CS active cnt %d is not 0 during reset\n", + hdev->cs_active_cnt); + + mutex_lock(&hdev->fpriv_list_lock); + + /* Make sure the context switch phase will run again */ + if (hdev->compute_ctx) { + atomic_set(&hdev->compute_ctx->thread_ctx_switch_token, 1); + hdev->compute_ctx->thread_ctx_switch_wait_token = 0; + } + + mutex_unlock(&hdev->fpriv_list_lock); + + /* Finished tear-down, starting to re-initialize */ + + if (hard_reset) { + hdev->device_cpu_disabled = false; + hdev->hard_reset_pending = false; + + if (hdev->kernel_ctx) { + dev_crit(hdev->dev, + "kernel ctx was alive during hard reset, something is terribly wrong\n"); + rc = -EBUSY; + goto out_err; + } + + rc = hl_mmu_init(hdev); + if (rc) { + dev_err(hdev->dev, + "Failed to initialize MMU S/W after hard reset\n"); + goto out_err; + } + + /* Allocate the kernel context */ + hdev->kernel_ctx = kzalloc(sizeof(*hdev->kernel_ctx), + GFP_KERNEL); + if (!hdev->kernel_ctx) { + rc = -ENOMEM; + goto out_err; + } + + hdev->compute_ctx = NULL; + + rc = hl_ctx_init(hdev, hdev->kernel_ctx, true); + if (rc) { + dev_err(hdev->dev, + "failed to init kernel ctx in hard reset\n"); + kfree(hdev->kernel_ctx); + hdev->kernel_ctx = NULL; + goto out_err; + } + } + + rc = hdev->asic_funcs->hw_init(hdev); + if (rc) { + dev_err(hdev->dev, + "failed to initialize the H/W after reset\n"); + goto out_err; + } + + hdev->disabled = false; + + /* Check that the communication with the device is working */ + rc = hdev->asic_funcs->test_queues(hdev); + if (rc) { + dev_err(hdev->dev, + "Failed to detect if device is alive after reset\n"); + goto out_err; + } + + if (hard_reset) { + rc = device_late_init(hdev); + if (rc) { + dev_err(hdev->dev, + "Failed late init after hard reset\n"); + goto out_err; + } + + rc = hl_vm_init(hdev); + if (rc) { + dev_err(hdev->dev, + "Failed to init memory module after hard reset\n"); + goto out_err; + } + + hl_set_max_power(hdev, hdev->max_power); + } else { + rc = hdev->asic_funcs->soft_reset_late_init(hdev); + if (rc) { + dev_err(hdev->dev, + "Failed late init after soft reset\n"); + goto out_err; + } + } + + atomic_set(&hdev->in_reset, 0); + + if (hard_reset) + hdev->hard_reset_cnt++; + else + hdev->soft_reset_cnt++; + + dev_warn(hdev->dev, "Successfully finished resetting the device\n"); + + return 0; + +out_err: + hdev->disabled = true; + + if (hard_reset) { + dev_err(hdev->dev, + "Failed to reset! Device is NOT usable\n"); + hdev->hard_reset_cnt++; + } else { + dev_err(hdev->dev, + "Failed to do soft-reset, trying hard reset\n"); + hdev->soft_reset_cnt++; + hard_reset = true; + goto again; + } + + atomic_set(&hdev->in_reset, 0); + + return rc; +} + +/* + * hl_device_init - main initialization function for habanalabs device + * + * @hdev: pointer to habanalabs device structure + * + * Allocate an id for the device, do early initialization and then call the + * ASIC specific initialization functions. Finally, create the cdev and the + * Linux device to expose it to the user + */ +int hl_device_init(struct hl_device *hdev, struct class *hclass) +{ + int i, rc, cq_cnt, cq_ready_cnt; + char *name; + bool add_cdev_sysfs_on_err = false; + + name = kasprintf(GFP_KERNEL, "hl%d", hdev->id / 2); + if (!name) { + rc = -ENOMEM; + goto out_disabled; + } + + /* Initialize cdev and device structures */ + rc = device_init_cdev(hdev, hclass, hdev->id, &hl_ops, name, + &hdev->cdev, &hdev->dev); + + kfree(name); + + if (rc) + goto out_disabled; + + name = kasprintf(GFP_KERNEL, "hl_controlD%d", hdev->id / 2); + if (!name) { + rc = -ENOMEM; + goto free_dev; + } + + /* Initialize cdev and device structures for control device */ + rc = device_init_cdev(hdev, hclass, hdev->id_control, &hl_ctrl_ops, + name, &hdev->cdev_ctrl, &hdev->dev_ctrl); + + kfree(name); + + if (rc) + goto free_dev; + + /* Initialize ASIC function pointers and perform early init */ + rc = device_early_init(hdev); + if (rc) + goto free_dev_ctrl; + + /* + * Start calling ASIC initialization. First S/W then H/W and finally + * late init + */ + rc = hdev->asic_funcs->sw_init(hdev); + if (rc) + goto early_fini; + + /* + * Initialize the H/W queues. Must be done before hw_init, because + * there the addresses of the kernel queue are being written to the + * registers of the device + */ + rc = hl_hw_queues_create(hdev); + if (rc) { + dev_err(hdev->dev, "failed to initialize kernel queues\n"); + goto sw_fini; + } + + cq_cnt = hdev->asic_prop.completion_queues_count; + + /* + * Initialize the completion queues. Must be done before hw_init, + * because there the addresses of the completion queues are being + * passed as arguments to request_irq + */ + if (cq_cnt) { + hdev->completion_queue = kcalloc(cq_cnt, + sizeof(*hdev->completion_queue), + GFP_KERNEL); + + if (!hdev->completion_queue) { + dev_err(hdev->dev, + "failed to allocate completion queues\n"); + rc = -ENOMEM; + goto hw_queues_destroy; + } + } + + for (i = 0, cq_ready_cnt = 0 ; i < cq_cnt ; i++, cq_ready_cnt++) { + rc = hl_cq_init(hdev, &hdev->completion_queue[i], + hdev->asic_funcs->get_queue_id_for_cq(hdev, i)); + if (rc) { + dev_err(hdev->dev, + "failed to initialize completion queue\n"); + goto cq_fini; + } + hdev->completion_queue[i].cq_idx = i; + } + + /* + * Initialize the event queue. Must be done before hw_init, + * because there the address of the event queue is being + * passed as argument to request_irq + */ + rc = hl_eq_init(hdev, &hdev->event_queue); + if (rc) { + dev_err(hdev->dev, "failed to initialize event queue\n"); + goto cq_fini; + } + + /* MMU S/W must be initialized before kernel context is created */ + rc = hl_mmu_init(hdev); + if (rc) { + dev_err(hdev->dev, "Failed to initialize MMU S/W structures\n"); + goto eq_fini; + } + + /* Allocate the kernel context */ + hdev->kernel_ctx = kzalloc(sizeof(*hdev->kernel_ctx), GFP_KERNEL); + if (!hdev->kernel_ctx) { + rc = -ENOMEM; + goto mmu_fini; + } + + hdev->compute_ctx = NULL; + + rc = hl_ctx_init(hdev, hdev->kernel_ctx, true); + if (rc) { + dev_err(hdev->dev, "failed to initialize kernel context\n"); + kfree(hdev->kernel_ctx); + goto mmu_fini; + } + + rc = hl_cb_pool_init(hdev); + if (rc) { + dev_err(hdev->dev, "failed to initialize CB pool\n"); + goto release_ctx; + } + + hl_debugfs_add_device(hdev); + + if (hdev->asic_funcs->get_hw_state(hdev) == HL_DEVICE_HW_STATE_DIRTY) { + dev_info(hdev->dev, + "H/W state is dirty, must reset before initializing\n"); + hdev->asic_funcs->halt_engines(hdev, true); + hdev->asic_funcs->hw_fini(hdev, true); + } + + /* + * From this point, in case of an error, add char devices and create + * sysfs nodes as part of the error flow, to allow debugging. + */ + add_cdev_sysfs_on_err = true; + + rc = hdev->asic_funcs->hw_init(hdev); + if (rc) { + dev_err(hdev->dev, "failed to initialize the H/W\n"); + rc = 0; + goto out_disabled; + } + + hdev->disabled = false; + + /* Check that the communication with the device is working */ + rc = hdev->asic_funcs->test_queues(hdev); + if (rc) { + dev_err(hdev->dev, "Failed to detect if device is alive\n"); + rc = 0; + goto out_disabled; + } + + rc = device_late_init(hdev); + if (rc) { + dev_err(hdev->dev, "Failed late initialization\n"); + rc = 0; + goto out_disabled; + } + + dev_info(hdev->dev, "Found %s device with %lluGB DRAM\n", + hdev->asic_name, + hdev->asic_prop.dram_size / 1024 / 1024 / 1024); + + rc = hl_vm_init(hdev); + if (rc) { + dev_err(hdev->dev, "Failed to initialize memory module\n"); + rc = 0; + goto out_disabled; + } + + /* + * Expose devices and sysfs nodes to user. + * From here there is no need to add char devices and create sysfs nodes + * in case of an error. + */ + add_cdev_sysfs_on_err = false; + rc = device_cdev_sysfs_add(hdev); + if (rc) { + dev_err(hdev->dev, + "Failed to add char devices and sysfs nodes\n"); + rc = 0; + goto out_disabled; + } + + /* + * hl_hwmon_init() must be called after device_late_init(), because only + * there we get the information from the device about which + * hwmon-related sensors the device supports. + * Furthermore, it must be done after adding the device to the system. + */ + rc = hl_hwmon_init(hdev); + if (rc) { + dev_err(hdev->dev, "Failed to initialize hwmon\n"); + rc = 0; + goto out_disabled; + } + + dev_notice(hdev->dev, + "Successfully added device to habanalabs driver\n"); + + hdev->init_done = true; + + return 0; + +release_ctx: + if (hl_ctx_put(hdev->kernel_ctx) != 1) + dev_err(hdev->dev, + "kernel ctx is still alive on initialization failure\n"); +mmu_fini: + hl_mmu_fini(hdev); +eq_fini: + hl_eq_fini(hdev, &hdev->event_queue); +cq_fini: + for (i = 0 ; i < cq_ready_cnt ; i++) + hl_cq_fini(hdev, &hdev->completion_queue[i]); + kfree(hdev->completion_queue); +hw_queues_destroy: + hl_hw_queues_destroy(hdev); +sw_fini: + hdev->asic_funcs->sw_fini(hdev); +early_fini: + device_early_fini(hdev); +free_dev_ctrl: + kfree(hdev->dev_ctrl); +free_dev: + kfree(hdev->dev); +out_disabled: + hdev->disabled = true; + if (add_cdev_sysfs_on_err) + device_cdev_sysfs_add(hdev); + if (hdev->pdev) + dev_err(&hdev->pdev->dev, + "Failed to initialize hl%d. Device is NOT usable !\n", + hdev->id / 2); + else + pr_err("Failed to initialize hl%d. Device is NOT usable !\n", + hdev->id / 2); + + return rc; +} + +/* + * hl_device_fini - main tear-down function for habanalabs device + * + * @hdev: pointer to habanalabs device structure + * + * Destroy the device, call ASIC fini functions and release the id + */ +void hl_device_fini(struct hl_device *hdev) +{ + int i, rc; + ktime_t timeout; + + dev_info(hdev->dev, "Removing device\n"); + + /* + * This function is competing with the reset function, so try to + * take the reset atomic and if we are already in middle of reset, + * wait until reset function is finished. Reset function is designed + * to always finish. However, in Gaudi, because of all the network + * ports, the hard reset could take between 10-30 seconds + */ + + timeout = ktime_add_us(ktime_get(), + HL_HARD_RESET_MAX_TIMEOUT * 1000 * 1000); + rc = atomic_cmpxchg(&hdev->in_reset, 0, 1); + while (rc) { + usleep_range(50, 200); + rc = atomic_cmpxchg(&hdev->in_reset, 0, 1); + if (ktime_compare(ktime_get(), timeout) > 0) { + WARN(1, "Failed to remove device because reset function did not finish\n"); + return; + } + } + + /* Mark device as disabled */ + hdev->disabled = true; + + /* Flush anyone that is inside the critical section of enqueue + * jobs to the H/W + */ + hdev->asic_funcs->hw_queues_lock(hdev); + hdev->asic_funcs->hw_queues_unlock(hdev); + + /* Flush anyone that is inside device open */ + mutex_lock(&hdev->fpriv_list_lock); + mutex_unlock(&hdev->fpriv_list_lock); + + hdev->hard_reset_pending = true; + + hl_hwmon_fini(hdev); + + device_late_fini(hdev); + + hl_debugfs_remove_device(hdev); + + /* + * Halt the engines and disable interrupts so we won't get any more + * completions from H/W and we won't have any accesses from the + * H/W to the host machine + */ + hdev->asic_funcs->halt_engines(hdev, true); + + /* Go over all the queues, release all CS and their jobs */ + hl_cs_rollback_all(hdev); + + /* Kill processes here after CS rollback. This is because the process + * can't really exit until all its CSs are done, which is what we + * do in cs rollback + */ + rc = device_kill_open_processes(hdev); + if (rc) + dev_crit(hdev->dev, "Failed to kill all open processes\n"); + + hl_cb_pool_fini(hdev); + + /* Release kernel context */ + if ((hdev->kernel_ctx) && (hl_ctx_put(hdev->kernel_ctx) != 1)) + dev_err(hdev->dev, "kernel ctx is still alive\n"); + + /* Reset the H/W. It will be in idle state after this returns */ + hdev->asic_funcs->hw_fini(hdev, true); + + hl_vm_fini(hdev); + + hl_mmu_fini(hdev); + + hl_eq_fini(hdev, &hdev->event_queue); + + for (i = 0 ; i < hdev->asic_prop.completion_queues_count ; i++) + hl_cq_fini(hdev, &hdev->completion_queue[i]); + kfree(hdev->completion_queue); + + hl_hw_queues_destroy(hdev); + + /* Call ASIC S/W finalize function */ + hdev->asic_funcs->sw_fini(hdev); + + device_early_fini(hdev); + + /* Hide devices and sysfs nodes from user */ + device_cdev_sysfs_del(hdev); + + pr_info("removed device successfully\n"); +} + +/* + * MMIO register access helper functions. + */ + +/* + * hl_rreg - Read an MMIO register + * + * @hdev: pointer to habanalabs device structure + * @reg: MMIO register offset (in bytes) + * + * Returns the value of the MMIO register we are asked to read + * + */ +inline u32 hl_rreg(struct hl_device *hdev, u32 reg) +{ + return readl(hdev->rmmio + reg); +} + +/* + * hl_wreg - Write to an MMIO register + * + * @hdev: pointer to habanalabs device structure + * @reg: MMIO register offset (in bytes) + * @val: 32-bit value + * + * Writes the 32-bit value into the MMIO register + * + */ +inline void hl_wreg(struct hl_device *hdev, u32 reg, u32 val) +{ + writel(val, hdev->rmmio + reg); +} diff --git a/drivers/misc/habanalabs/common/firmware_if.c b/drivers/misc/habanalabs/common/firmware_if.c new file mode 100644 index 000000000000..b2b84510b932 --- /dev/null +++ b/drivers/misc/habanalabs/common/firmware_if.c @@ -0,0 +1,589 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright 2016-2019 HabanaLabs, Ltd. + * All Rights Reserved. + */ + +#include "habanalabs.h" +#include "include/common/hl_boot_if.h" + +#include +#include +#include +#include + +/** + * hl_fw_load_fw_to_device() - Load F/W code to device's memory. + * + * @hdev: pointer to hl_device structure. + * @fw_name: the firmware image name + * @dst: IO memory mapped address space to copy firmware to + * + * Copy fw code from firmware file to device memory. + * + * Return: 0 on success, non-zero for failure. + */ +int hl_fw_load_fw_to_device(struct hl_device *hdev, const char *fw_name, + void __iomem *dst) +{ + const struct firmware *fw; + const u64 *fw_data; + size_t fw_size; + int rc; + + rc = request_firmware(&fw, fw_name, hdev->dev); + if (rc) { + dev_err(hdev->dev, "Firmware file %s is not found!\n", fw_name); + goto out; + } + + fw_size = fw->size; + if ((fw_size % 4) != 0) { + dev_err(hdev->dev, "Illegal %s firmware size %zu\n", + fw_name, fw_size); + rc = -EINVAL; + goto out; + } + + dev_dbg(hdev->dev, "%s firmware size == %zu\n", fw_name, fw_size); + + fw_data = (const u64 *) fw->data; + + memcpy_toio(dst, fw_data, fw_size); + +out: + release_firmware(fw); + return rc; +} + +int hl_fw_send_pci_access_msg(struct hl_device *hdev, u32 opcode) +{ + struct armcp_packet pkt = {}; + + pkt.ctl = cpu_to_le32(opcode << ARMCP_PKT_CTL_OPCODE_SHIFT); + + return hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, + sizeof(pkt), HL_DEVICE_TIMEOUT_USEC, NULL); +} + +int hl_fw_send_cpu_message(struct hl_device *hdev, u32 hw_queue_id, u32 *msg, + u16 len, u32 timeout, long *result) +{ + struct armcp_packet *pkt; + dma_addr_t pkt_dma_addr; + u32 tmp; + int rc = 0; + + pkt = hdev->asic_funcs->cpu_accessible_dma_pool_alloc(hdev, len, + &pkt_dma_addr); + if (!pkt) { + dev_err(hdev->dev, + "Failed to allocate DMA memory for packet to CPU\n"); + return -ENOMEM; + } + + memcpy(pkt, msg, len); + + mutex_lock(&hdev->send_cpu_message_lock); + + if (hdev->disabled) + goto out; + + if (hdev->device_cpu_disabled) { + rc = -EIO; + goto out; + } + + rc = hl_hw_queue_send_cb_no_cmpl(hdev, hw_queue_id, len, pkt_dma_addr); + if (rc) { + dev_err(hdev->dev, "Failed to send CB on CPU PQ (%d)\n", rc); + goto out; + } + + rc = hl_poll_timeout_memory(hdev, &pkt->fence, tmp, + (tmp == ARMCP_PACKET_FENCE_VAL), 1000, + timeout, true); + + hl_hw_queue_inc_ci_kernel(hdev, hw_queue_id); + + if (rc == -ETIMEDOUT) { + dev_err(hdev->dev, "Device CPU packet timeout (0x%x)\n", tmp); + hdev->device_cpu_disabled = true; + goto out; + } + + tmp = le32_to_cpu(pkt->ctl); + + rc = (tmp & ARMCP_PKT_CTL_RC_MASK) >> ARMCP_PKT_CTL_RC_SHIFT; + if (rc) { + dev_err(hdev->dev, "F/W ERROR %d for CPU packet %d\n", + rc, + (tmp & ARMCP_PKT_CTL_OPCODE_MASK) + >> ARMCP_PKT_CTL_OPCODE_SHIFT); + rc = -EIO; + } else if (result) { + *result = (long) le64_to_cpu(pkt->result); + } + +out: + mutex_unlock(&hdev->send_cpu_message_lock); + + hdev->asic_funcs->cpu_accessible_dma_pool_free(hdev, len, pkt); + + return rc; +} + +int hl_fw_unmask_irq(struct hl_device *hdev, u16 event_type) +{ + struct armcp_packet pkt; + long result; + int rc; + + memset(&pkt, 0, sizeof(pkt)); + + pkt.ctl = cpu_to_le32(ARMCP_PACKET_UNMASK_RAZWI_IRQ << + ARMCP_PKT_CTL_OPCODE_SHIFT); + pkt.value = cpu_to_le64(event_type); + + rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), + HL_DEVICE_TIMEOUT_USEC, &result); + + if (rc) + dev_err(hdev->dev, "failed to unmask RAZWI IRQ %d", event_type); + + return rc; +} + +int hl_fw_unmask_irq_arr(struct hl_device *hdev, const u32 *irq_arr, + size_t irq_arr_size) +{ + struct armcp_unmask_irq_arr_packet *pkt; + size_t total_pkt_size; + long result; + int rc; + + total_pkt_size = sizeof(struct armcp_unmask_irq_arr_packet) + + irq_arr_size; + + /* data should be aligned to 8 bytes in order to ArmCP to copy it */ + total_pkt_size = (total_pkt_size + 0x7) & ~0x7; + + /* total_pkt_size is casted to u16 later on */ + if (total_pkt_size > USHRT_MAX) { + dev_err(hdev->dev, "too many elements in IRQ array\n"); + return -EINVAL; + } + + pkt = kzalloc(total_pkt_size, GFP_KERNEL); + if (!pkt) + return -ENOMEM; + + pkt->length = cpu_to_le32(irq_arr_size / sizeof(irq_arr[0])); + memcpy(&pkt->irqs, irq_arr, irq_arr_size); + + pkt->armcp_pkt.ctl = cpu_to_le32(ARMCP_PACKET_UNMASK_RAZWI_IRQ_ARRAY << + ARMCP_PKT_CTL_OPCODE_SHIFT); + + rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) pkt, + total_pkt_size, HL_DEVICE_TIMEOUT_USEC, &result); + + if (rc) + dev_err(hdev->dev, "failed to unmask IRQ array\n"); + + kfree(pkt); + + return rc; +} + +int hl_fw_test_cpu_queue(struct hl_device *hdev) +{ + struct armcp_packet test_pkt = {}; + long result; + int rc; + + test_pkt.ctl = cpu_to_le32(ARMCP_PACKET_TEST << + ARMCP_PKT_CTL_OPCODE_SHIFT); + test_pkt.value = cpu_to_le64(ARMCP_PACKET_FENCE_VAL); + + rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &test_pkt, + sizeof(test_pkt), HL_DEVICE_TIMEOUT_USEC, &result); + + if (!rc) { + if (result != ARMCP_PACKET_FENCE_VAL) + dev_err(hdev->dev, + "CPU queue test failed (0x%08lX)\n", result); + } else { + dev_err(hdev->dev, "CPU queue test failed, error %d\n", rc); + } + + return rc; +} + +void *hl_fw_cpu_accessible_dma_pool_alloc(struct hl_device *hdev, size_t size, + dma_addr_t *dma_handle) +{ + u64 kernel_addr; + + kernel_addr = gen_pool_alloc(hdev->cpu_accessible_dma_pool, size); + + *dma_handle = hdev->cpu_accessible_dma_address + + (kernel_addr - (u64) (uintptr_t) hdev->cpu_accessible_dma_mem); + + return (void *) (uintptr_t) kernel_addr; +} + +void hl_fw_cpu_accessible_dma_pool_free(struct hl_device *hdev, size_t size, + void *vaddr) +{ + gen_pool_free(hdev->cpu_accessible_dma_pool, (u64) (uintptr_t) vaddr, + size); +} + +int hl_fw_send_heartbeat(struct hl_device *hdev) +{ + struct armcp_packet hb_pkt = {}; + long result; + int rc; + + hb_pkt.ctl = cpu_to_le32(ARMCP_PACKET_TEST << + ARMCP_PKT_CTL_OPCODE_SHIFT); + hb_pkt.value = cpu_to_le64(ARMCP_PACKET_FENCE_VAL); + + rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &hb_pkt, + sizeof(hb_pkt), HL_DEVICE_TIMEOUT_USEC, &result); + + if ((rc) || (result != ARMCP_PACKET_FENCE_VAL)) + rc = -EIO; + + return rc; +} + +int hl_fw_armcp_info_get(struct hl_device *hdev) +{ + struct asic_fixed_properties *prop = &hdev->asic_prop; + struct armcp_packet pkt = {}; + void *armcp_info_cpu_addr; + dma_addr_t armcp_info_dma_addr; + long result; + int rc; + + armcp_info_cpu_addr = + hdev->asic_funcs->cpu_accessible_dma_pool_alloc(hdev, + sizeof(struct armcp_info), + &armcp_info_dma_addr); + if (!armcp_info_cpu_addr) { + dev_err(hdev->dev, + "Failed to allocate DMA memory for ArmCP info packet\n"); + return -ENOMEM; + } + + memset(armcp_info_cpu_addr, 0, sizeof(struct armcp_info)); + + pkt.ctl = cpu_to_le32(ARMCP_PACKET_INFO_GET << + ARMCP_PKT_CTL_OPCODE_SHIFT); + pkt.addr = cpu_to_le64(armcp_info_dma_addr); + pkt.data_max_size = cpu_to_le32(sizeof(struct armcp_info)); + + rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), + HL_ARMCP_INFO_TIMEOUT_USEC, &result); + if (rc) { + dev_err(hdev->dev, + "Failed to handle ArmCP info pkt, error %d\n", rc); + goto out; + } + + memcpy(&prop->armcp_info, armcp_info_cpu_addr, + sizeof(prop->armcp_info)); + + rc = hl_build_hwmon_channel_info(hdev, prop->armcp_info.sensors); + if (rc) { + dev_err(hdev->dev, + "Failed to build hwmon channel info, error %d\n", rc); + rc = -EFAULT; + goto out; + } + +out: + hdev->asic_funcs->cpu_accessible_dma_pool_free(hdev, + sizeof(struct armcp_info), armcp_info_cpu_addr); + + return rc; +} + +int hl_fw_get_eeprom_data(struct hl_device *hdev, void *data, size_t max_size) +{ + struct armcp_packet pkt = {}; + void *eeprom_info_cpu_addr; + dma_addr_t eeprom_info_dma_addr; + long result; + int rc; + + eeprom_info_cpu_addr = + hdev->asic_funcs->cpu_accessible_dma_pool_alloc(hdev, + max_size, &eeprom_info_dma_addr); + if (!eeprom_info_cpu_addr) { + dev_err(hdev->dev, + "Failed to allocate DMA memory for ArmCP EEPROM packet\n"); + return -ENOMEM; + } + + memset(eeprom_info_cpu_addr, 0, max_size); + + pkt.ctl = cpu_to_le32(ARMCP_PACKET_EEPROM_DATA_GET << + ARMCP_PKT_CTL_OPCODE_SHIFT); + pkt.addr = cpu_to_le64(eeprom_info_dma_addr); + pkt.data_max_size = cpu_to_le32(max_size); + + rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), + HL_ARMCP_EEPROM_TIMEOUT_USEC, &result); + + if (rc) { + dev_err(hdev->dev, + "Failed to handle ArmCP EEPROM packet, error %d\n", rc); + goto out; + } + + /* result contains the actual size */ + memcpy(data, eeprom_info_cpu_addr, min((size_t)result, max_size)); + +out: + hdev->asic_funcs->cpu_accessible_dma_pool_free(hdev, max_size, + eeprom_info_cpu_addr); + + return rc; +} + +static void fw_read_errors(struct hl_device *hdev, u32 boot_err0_reg) +{ + u32 err_val; + + /* Some of the firmware status codes are deprecated in newer f/w + * versions. In those versions, the errors are reported + * in different registers. Therefore, we need to check those + * registers and print the exact errors. Moreover, there + * may be multiple errors, so we need to report on each error + * separately. Some of the error codes might indicate a state + * that is not an error per-se, but it is an error in production + * environment + */ + err_val = RREG32(boot_err0_reg); + if (!(err_val & CPU_BOOT_ERR0_ENABLED)) + return; + + if (err_val & CPU_BOOT_ERR0_DRAM_INIT_FAIL) + dev_err(hdev->dev, + "Device boot error - DRAM initialization failed\n"); + if (err_val & CPU_BOOT_ERR0_FIT_CORRUPTED) + dev_err(hdev->dev, "Device boot error - FIT image corrupted\n"); + if (err_val & CPU_BOOT_ERR0_TS_INIT_FAIL) + dev_err(hdev->dev, + "Device boot error - Thermal Sensor initialization failed\n"); + if (err_val & CPU_BOOT_ERR0_DRAM_SKIPPED) + dev_warn(hdev->dev, + "Device boot warning - Skipped DRAM initialization\n"); + if (err_val & CPU_BOOT_ERR0_BMC_WAIT_SKIPPED) + dev_warn(hdev->dev, + "Device boot error - Skipped waiting for BMC\n"); + if (err_val & CPU_BOOT_ERR0_NIC_DATA_NOT_RDY) + dev_err(hdev->dev, + "Device boot error - Serdes data from BMC not available\n"); + if (err_val & CPU_BOOT_ERR0_NIC_FW_FAIL) + dev_err(hdev->dev, + "Device boot error - NIC F/W initialization failed\n"); +} + +static void hl_detect_cpu_boot_status(struct hl_device *hdev, u32 status) +{ + switch (status) { + case CPU_BOOT_STATUS_NA: + dev_err(hdev->dev, + "Device boot error - BTL did NOT run\n"); + break; + case CPU_BOOT_STATUS_IN_WFE: + dev_err(hdev->dev, + "Device boot error - Stuck inside WFE loop\n"); + break; + case CPU_BOOT_STATUS_IN_BTL: + dev_err(hdev->dev, + "Device boot error - Stuck in BTL\n"); + break; + case CPU_BOOT_STATUS_IN_PREBOOT: + dev_err(hdev->dev, + "Device boot error - Stuck in Preboot\n"); + break; + case CPU_BOOT_STATUS_IN_SPL: + dev_err(hdev->dev, + "Device boot error - Stuck in SPL\n"); + break; + case CPU_BOOT_STATUS_IN_UBOOT: + dev_err(hdev->dev, + "Device boot error - Stuck in u-boot\n"); + break; + case CPU_BOOT_STATUS_DRAM_INIT_FAIL: + dev_err(hdev->dev, + "Device boot error - DRAM initialization failed\n"); + break; + case CPU_BOOT_STATUS_UBOOT_NOT_READY: + dev_err(hdev->dev, + "Device boot error - u-boot stopped by user\n"); + break; + case CPU_BOOT_STATUS_TS_INIT_FAIL: + dev_err(hdev->dev, + "Device boot error - Thermal Sensor initialization failed\n"); + break; + default: + dev_err(hdev->dev, + "Device boot error - Invalid status code %d\n", + status); + break; + } +} + +int hl_fw_init_cpu(struct hl_device *hdev, u32 cpu_boot_status_reg, + u32 msg_to_cpu_reg, u32 cpu_msg_status_reg, + u32 boot_err0_reg, bool skip_bmc, + u32 cpu_timeout, u32 boot_fit_timeout) +{ + u32 status; + int rc; + + dev_info(hdev->dev, "Going to wait for device boot (up to %lds)\n", + cpu_timeout / USEC_PER_SEC); + + /* Wait for boot FIT request */ + rc = hl_poll_timeout( + hdev, + cpu_boot_status_reg, + status, + status == CPU_BOOT_STATUS_WAITING_FOR_BOOT_FIT, + 10000, + boot_fit_timeout); + + if (rc) { + dev_dbg(hdev->dev, + "No boot fit request received, resuming boot\n"); + } else { + rc = hdev->asic_funcs->load_boot_fit_to_device(hdev); + if (rc) + goto out; + + /* Clear device CPU message status */ + WREG32(cpu_msg_status_reg, CPU_MSG_CLR); + + /* Signal device CPU that boot loader is ready */ + WREG32(msg_to_cpu_reg, KMD_MSG_FIT_RDY); + + /* Poll for CPU device ack */ + rc = hl_poll_timeout( + hdev, + cpu_msg_status_reg, + status, + status == CPU_MSG_OK, + 10000, + boot_fit_timeout); + + if (rc) { + dev_err(hdev->dev, + "Timeout waiting for boot fit load ack\n"); + goto out; + } + + /* Clear message */ + WREG32(msg_to_cpu_reg, KMD_MSG_NA); + } + + /* Make sure CPU boot-loader is running */ + rc = hl_poll_timeout( + hdev, + cpu_boot_status_reg, + status, + (status == CPU_BOOT_STATUS_DRAM_RDY) || + (status == CPU_BOOT_STATUS_NIC_FW_RDY) || + (status == CPU_BOOT_STATUS_READY_TO_BOOT) || + (status == CPU_BOOT_STATUS_SRAM_AVAIL), + 10000, + cpu_timeout); + + /* Read U-Boot, preboot versions now in case we will later fail */ + hdev->asic_funcs->read_device_fw_version(hdev, FW_COMP_UBOOT); + hdev->asic_funcs->read_device_fw_version(hdev, FW_COMP_PREBOOT); + + /* Some of the status codes below are deprecated in newer f/w + * versions but we keep them here for backward compatibility + */ + if (rc) { + hl_detect_cpu_boot_status(hdev, status); + rc = -EIO; + goto out; + } + + if (!hdev->fw_loading) { + dev_info(hdev->dev, "Skip loading FW\n"); + goto out; + } + + if (status == CPU_BOOT_STATUS_SRAM_AVAIL) + goto out; + + dev_info(hdev->dev, + "Loading firmware to device, may take some time...\n"); + + rc = hdev->asic_funcs->load_firmware_to_device(hdev); + if (rc) + goto out; + + if (skip_bmc) { + WREG32(msg_to_cpu_reg, KMD_MSG_SKIP_BMC); + + rc = hl_poll_timeout( + hdev, + cpu_boot_status_reg, + status, + (status == CPU_BOOT_STATUS_BMC_WAITING_SKIPPED), + 10000, + cpu_timeout); + + if (rc) { + dev_err(hdev->dev, + "Failed to get ACK on skipping BMC, %d\n", + status); + WREG32(msg_to_cpu_reg, KMD_MSG_NA); + rc = -EIO; + goto out; + } + } + + WREG32(msg_to_cpu_reg, KMD_MSG_FIT_RDY); + + rc = hl_poll_timeout( + hdev, + cpu_boot_status_reg, + status, + (status == CPU_BOOT_STATUS_SRAM_AVAIL), + 10000, + cpu_timeout); + + /* Clear message */ + WREG32(msg_to_cpu_reg, KMD_MSG_NA); + + if (rc) { + if (status == CPU_BOOT_STATUS_FIT_CORRUPTED) + dev_err(hdev->dev, + "Device reports FIT image is corrupted\n"); + else + dev_err(hdev->dev, + "Failed to load firmware to device, %d\n", + status); + + rc = -EIO; + goto out; + } + + dev_info(hdev->dev, "Successfully loaded firmware to device\n"); + +out: + fw_read_errors(hdev, boot_err0_reg); + + return rc; +} diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h new file mode 100644 index 000000000000..82532f1f94cb --- /dev/null +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -0,0 +1,1948 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright 2016-2019 HabanaLabs, Ltd. + * All Rights Reserved. + * + */ + +#ifndef HABANALABSP_H_ +#define HABANALABSP_H_ + +#include "include/common/armcp_if.h" +#include "include/common/qman_if.h" +#include + +#include +#include +#include +#include +#include +#include +#include + +#define HL_NAME "habanalabs" + +#define HL_MMAP_CB_MASK (0x8000000000000000ull >> PAGE_SHIFT) + +#define HL_PENDING_RESET_PER_SEC 30 + +#define HL_HARD_RESET_MAX_TIMEOUT 120 + +#define HL_DEVICE_TIMEOUT_USEC 1000000 /* 1 s */ + +#define HL_HEARTBEAT_PER_USEC 5000000 /* 5 s */ + +#define HL_PLL_LOW_JOB_FREQ_USEC 5000000 /* 5 s */ + +#define HL_ARMCP_INFO_TIMEOUT_USEC 10000000 /* 10s */ +#define HL_ARMCP_EEPROM_TIMEOUT_USEC 10000000 /* 10s */ + +#define HL_PCI_ELBI_TIMEOUT_MSEC 10 /* 10ms */ + +#define HL_SIM_MAX_TIMEOUT_US 10000000 /* 10s */ + +#define HL_IDLE_BUSY_TS_ARR_SIZE 4096 + +/* Memory */ +#define MEM_HASH_TABLE_BITS 7 /* 1 << 7 buckets */ + +/* MMU */ +#define MMU_HASH_TABLE_BITS 7 /* 1 << 7 buckets */ + +/* + * HL_RSVD_SOBS 'sync stream' reserved sync objects per QMAN stream + * HL_RSVD_MONS 'sync stream' reserved monitors per QMAN stream + */ +#define HL_RSVD_SOBS 4 +#define HL_RSVD_MONS 2 + +#define HL_RSVD_SOBS_IN_USE 2 +#define HL_RSVD_MONS_IN_USE 1 + +#define HL_MAX_SOB_VAL (1 << 15) + +#define IS_POWER_OF_2(n) (n != 0 && ((n & (n - 1)) == 0)) +#define IS_MAX_PENDING_CS_VALID(n) (IS_POWER_OF_2(n) && (n > 1)) + +#define HL_PCI_NUM_BARS 6 + +/** + * struct pgt_info - MMU hop page info. + * @node: hash linked-list node for the pgts shadow hash of pgts. + * @phys_addr: physical address of the pgt. + * @shadow_addr: shadow hop in the host. + * @ctx: pointer to the owner ctx. + * @num_of_ptes: indicates how many ptes are used in the pgt. + * + * The MMU page tables hierarchy is placed on the DRAM. When a new level (hop) + * is needed during mapping, a new page is allocated and this structure holds + * its essential information. During unmapping, if no valid PTEs remained in the + * page, it is freed with its pgt_info structure. + */ +struct pgt_info { + struct hlist_node node; + u64 phys_addr; + u64 shadow_addr; + struct hl_ctx *ctx; + int num_of_ptes; +}; + +struct hl_device; +struct hl_fpriv; + +/** + * enum hl_pci_match_mode - pci match mode per region + * @PCI_ADDRESS_MATCH_MODE: address match mode + * @PCI_BAR_MATCH_MODE: bar match mode + */ +enum hl_pci_match_mode { + PCI_ADDRESS_MATCH_MODE, + PCI_BAR_MATCH_MODE +}; + +/** + * enum hl_fw_component - F/W components to read version through registers. + * @FW_COMP_UBOOT: u-boot. + * @FW_COMP_PREBOOT: preboot. + */ +enum hl_fw_component { + FW_COMP_UBOOT, + FW_COMP_PREBOOT +}; + +/** + * enum hl_queue_type - Supported QUEUE types. + * @QUEUE_TYPE_NA: queue is not available. + * @QUEUE_TYPE_EXT: external queue which is a DMA channel that may access the + * host. + * @QUEUE_TYPE_INT: internal queue that performs DMA inside the device's + * memories and/or operates the compute engines. + * @QUEUE_TYPE_CPU: S/W queue for communication with the device's CPU. + * @QUEUE_TYPE_HW: queue of DMA and compute engines jobs, for which completion + * notifications are sent by H/W. + */ +enum hl_queue_type { + QUEUE_TYPE_NA, + QUEUE_TYPE_EXT, + QUEUE_TYPE_INT, + QUEUE_TYPE_CPU, + QUEUE_TYPE_HW +}; + +enum hl_cs_type { + CS_TYPE_DEFAULT, + CS_TYPE_SIGNAL, + CS_TYPE_WAIT +}; + +/* + * struct hl_inbound_pci_region - inbound region descriptor + * @mode: pci match mode for this region + * @addr: region target address + * @size: region size in bytes + * @offset_in_bar: offset within bar (address match mode) + * @bar: bar id + */ +struct hl_inbound_pci_region { + enum hl_pci_match_mode mode; + u64 addr; + u64 size; + u64 offset_in_bar; + u8 bar; +}; + +/* + * struct hl_outbound_pci_region - outbound region descriptor + * @addr: region target address + * @size: region size in bytes + */ +struct hl_outbound_pci_region { + u64 addr; + u64 size; +}; + +/* + * struct hl_hw_sob - H/W SOB info. + * @hdev: habanalabs device structure. + * @kref: refcount of this SOB. The SOB will reset once the refcount is zero. + * @sob_id: id of this SOB. + * @q_idx: the H/W queue that uses this SOB. + */ +struct hl_hw_sob { + struct hl_device *hdev; + struct kref kref; + u32 sob_id; + u32 q_idx; +}; + +/** + * struct hw_queue_properties - queue information. + * @type: queue type. + * @driver_only: true if only the driver is allowed to send a job to this queue, + * false otherwise. + * @requires_kernel_cb: true if a CB handle must be provided for jobs on this + * queue, false otherwise (a CB address must be provided). + * @supports_sync_stream: True if queue supports sync stream + */ +struct hw_queue_properties { + enum hl_queue_type type; + u8 driver_only; + u8 requires_kernel_cb; + u8 supports_sync_stream; +}; + +/** + * enum vm_type_t - virtual memory mapping request information. + * @VM_TYPE_USERPTR: mapping of user memory to device virtual address. + * @VM_TYPE_PHYS_PACK: mapping of DRAM memory to device virtual address. + */ +enum vm_type_t { + VM_TYPE_USERPTR = 0x1, + VM_TYPE_PHYS_PACK = 0x2 +}; + +/** + * enum hl_device_hw_state - H/W device state. use this to understand whether + * to do reset before hw_init or not + * @HL_DEVICE_HW_STATE_CLEAN: H/W state is clean. i.e. after hard reset + * @HL_DEVICE_HW_STATE_DIRTY: H/W state is dirty. i.e. we started to execute + * hw_init + */ +enum hl_device_hw_state { + HL_DEVICE_HW_STATE_CLEAN = 0, + HL_DEVICE_HW_STATE_DIRTY +}; + +/** + * struct hl_mmu_properties - ASIC specific MMU address translation properties. + * @start_addr: virtual start address of the memory region. + * @end_addr: virtual end address of the memory region. + * @hop0_shift: shift of hop 0 mask. + * @hop1_shift: shift of hop 1 mask. + * @hop2_shift: shift of hop 2 mask. + * @hop3_shift: shift of hop 3 mask. + * @hop4_shift: shift of hop 4 mask. + * @hop0_mask: mask to get the PTE address in hop 0. + * @hop1_mask: mask to get the PTE address in hop 1. + * @hop2_mask: mask to get the PTE address in hop 2. + * @hop3_mask: mask to get the PTE address in hop 3. + * @hop4_mask: mask to get the PTE address in hop 4. + * @page_size: default page size used to allocate memory. + */ +struct hl_mmu_properties { + u64 start_addr; + u64 end_addr; + u64 hop0_shift; + u64 hop1_shift; + u64 hop2_shift; + u64 hop3_shift; + u64 hop4_shift; + u64 hop0_mask; + u64 hop1_mask; + u64 hop2_mask; + u64 hop3_mask; + u64 hop4_mask; + u32 page_size; +}; + +/** + * struct asic_fixed_properties - ASIC specific immutable properties. + * @hw_queues_props: H/W queues properties. + * @armcp_info: received various information from ArmCP regarding the H/W, e.g. + * available sensors. + * @uboot_ver: F/W U-boot version. + * @preboot_ver: F/W Preboot version. + * @dmmu: DRAM MMU address translation properties. + * @pmmu: PCI (host) MMU address translation properties. + * @pmmu_huge: PCI (host) MMU address translation properties for memory + * allocated with huge pages. + * @sram_base_address: SRAM physical start address. + * @sram_end_address: SRAM physical end address. + * @sram_user_base_address - SRAM physical start address for user access. + * @dram_base_address: DRAM physical start address. + * @dram_end_address: DRAM physical end address. + * @dram_user_base_address: DRAM physical start address for user access. + * @dram_size: DRAM total size. + * @dram_pci_bar_size: size of PCI bar towards DRAM. + * @max_power_default: max power of the device after reset + * @dram_size_for_default_page_mapping: DRAM size needed to map to avoid page + * fault. + * @pcie_dbi_base_address: Base address of the PCIE_DBI block. + * @pcie_aux_dbi_reg_addr: Address of the PCIE_AUX DBI register. + * @mmu_pgt_addr: base physical address in DRAM of MMU page tables. + * @mmu_dram_default_page_addr: DRAM default page physical address. + * @mmu_pgt_size: MMU page tables total size. + * @mmu_pte_size: PTE size in MMU page tables. + * @mmu_hop_table_size: MMU hop table size. + * @mmu_hop0_tables_total_size: total size of MMU hop0 tables. + * @dram_page_size: page size for MMU DRAM allocation. + * @cfg_size: configuration space size on SRAM. + * @sram_size: total size of SRAM. + * @max_asid: maximum number of open contexts (ASIDs). + * @num_of_events: number of possible internal H/W IRQs. + * @psoc_pci_pll_nr: PCI PLL NR value. + * @psoc_pci_pll_nf: PCI PLL NF value. + * @psoc_pci_pll_od: PCI PLL OD value. + * @psoc_pci_pll_div_factor: PCI PLL DIV FACTOR 1 value. + * @psoc_timestamp_frequency: frequency of the psoc timestamp clock. + * @high_pll: high PLL frequency used by the device. + * @cb_pool_cb_cnt: number of CBs in the CB pool. + * @cb_pool_cb_size: size of each CB in the CB pool. + * @max_pending_cs: maximum of concurrent pending command submissions + * @max_queues: maximum amount of queues in the system + * @sync_stream_first_sob: first sync object available for sync stream use + * @sync_stream_first_mon: first monitor available for sync stream use + * @tpc_enabled_mask: which TPCs are enabled. + * @completion_queues_count: number of completion queues. + */ +struct asic_fixed_properties { + struct hw_queue_properties *hw_queues_props; + struct armcp_info armcp_info; + char uboot_ver[VERSION_MAX_LEN]; + char preboot_ver[VERSION_MAX_LEN]; + struct hl_mmu_properties dmmu; + struct hl_mmu_properties pmmu; + struct hl_mmu_properties pmmu_huge; + u64 sram_base_address; + u64 sram_end_address; + u64 sram_user_base_address; + u64 dram_base_address; + u64 dram_end_address; + u64 dram_user_base_address; + u64 dram_size; + u64 dram_pci_bar_size; + u64 max_power_default; + u64 dram_size_for_default_page_mapping; + u64 pcie_dbi_base_address; + u64 pcie_aux_dbi_reg_addr; + u64 mmu_pgt_addr; + u64 mmu_dram_default_page_addr; + u32 mmu_pgt_size; + u32 mmu_pte_size; + u32 mmu_hop_table_size; + u32 mmu_hop0_tables_total_size; + u32 dram_page_size; + u32 cfg_size; + u32 sram_size; + u32 max_asid; + u32 num_of_events; + u32 psoc_pci_pll_nr; + u32 psoc_pci_pll_nf; + u32 psoc_pci_pll_od; + u32 psoc_pci_pll_div_factor; + u32 psoc_timestamp_frequency; + u32 high_pll; + u32 cb_pool_cb_cnt; + u32 cb_pool_cb_size; + u32 max_pending_cs; + u32 max_queues; + u16 sync_stream_first_sob; + u16 sync_stream_first_mon; + u8 tpc_enabled_mask; + u8 completion_queues_count; +}; + +/** + * struct hl_cs_compl - command submission completion object. + * @base_fence: kernel fence object. + * @lock: spinlock to protect fence. + * @hdev: habanalabs device structure. + * @hw_sob: the H/W SOB used in this signal/wait CS. + * @cs_seq: command submission sequence number. + * @type: type of the CS - signal/wait. + * @sob_val: the SOB value that is used in this signal/wait CS. + */ +struct hl_cs_compl { + struct dma_fence base_fence; + spinlock_t lock; + struct hl_device *hdev; + struct hl_hw_sob *hw_sob; + u64 cs_seq; + enum hl_cs_type type; + u16 sob_val; +}; + +/* + * Command Buffers + */ + +/** + * struct hl_cb_mgr - describes a Command Buffer Manager. + * @cb_lock: protects cb_handles. + * @cb_handles: an idr to hold all command buffer handles. + */ +struct hl_cb_mgr { + spinlock_t cb_lock; + struct idr cb_handles; /* protected by cb_lock */ +}; + +/** + * struct hl_cb - describes a Command Buffer. + * @refcount: reference counter for usage of the CB. + * @hdev: pointer to device this CB belongs to. + * @lock: spinlock to protect mmap/cs flows. + * @debugfs_list: node in debugfs list of command buffers. + * @pool_list: node in pool list of command buffers. + * @kernel_address: Holds the CB's kernel virtual address. + * @bus_address: Holds the CB's DMA address. + * @mmap_size: Holds the CB's size that was mmaped. + * @size: holds the CB's size. + * @id: the CB's ID. + * @cs_cnt: holds number of CS that this CB participates in. + * @ctx_id: holds the ID of the owner's context. + * @mmap: true if the CB is currently mmaped to user. + * @is_pool: true if CB was acquired from the pool, false otherwise. + */ +struct hl_cb { + struct kref refcount; + struct hl_device *hdev; + spinlock_t lock; + struct list_head debugfs_list; + struct list_head pool_list; + u64 kernel_address; + dma_addr_t bus_address; + u32 mmap_size; + u32 size; + u32 id; + u32 cs_cnt; + u32 ctx_id; + u8 mmap; + u8 is_pool; +}; + + +/* + * QUEUES + */ + +struct hl_cs_job; + +/* Queue length of external and HW queues */ +#define HL_QUEUE_LENGTH 4096 +#define HL_QUEUE_SIZE_IN_BYTES (HL_QUEUE_LENGTH * HL_BD_SIZE) + +#if (HL_MAX_JOBS_PER_CS > HL_QUEUE_LENGTH) +#error "HL_QUEUE_LENGTH must be greater than HL_MAX_JOBS_PER_CS" +#endif + +/* HL_CQ_LENGTH is in units of struct hl_cq_entry */ +#define HL_CQ_LENGTH HL_QUEUE_LENGTH +#define HL_CQ_SIZE_IN_BYTES (HL_CQ_LENGTH * HL_CQ_ENTRY_SIZE) + +/* Must be power of 2 */ +#define HL_EQ_LENGTH 64 +#define HL_EQ_SIZE_IN_BYTES (HL_EQ_LENGTH * HL_EQ_ENTRY_SIZE) + +/* Host <-> ArmCP shared memory size */ +#define HL_CPU_ACCESSIBLE_MEM_SIZE SZ_2M + +/** + * struct hl_hw_queue - describes a H/W transport queue. + * @hw_sob: array of the used H/W SOBs by this H/W queue. + * @shadow_queue: pointer to a shadow queue that holds pointers to jobs. + * @queue_type: type of queue. + * @kernel_address: holds the queue's kernel virtual address. + * @bus_address: holds the queue's DMA address. + * @pi: holds the queue's pi value. + * @ci: holds the queue's ci value, AS CALCULATED BY THE DRIVER (not real ci). + * @hw_queue_id: the id of the H/W queue. + * @cq_id: the id for the corresponding CQ for this H/W queue. + * @msi_vec: the IRQ number of the H/W queue. + * @int_queue_len: length of internal queue (number of entries). + * @next_sob_val: the next value to use for the currently used SOB. + * @base_sob_id: the base SOB id of the SOBs used by this queue. + * @base_mon_id: the base MON id of the MONs used by this queue. + * @valid: is the queue valid (we have array of 32 queues, not all of them + * exist). + * @curr_sob_offset: the id offset to the currently used SOB from the + * HL_RSVD_SOBS that are being used by this queue. + * @supports_sync_stream: True if queue supports sync stream + */ +struct hl_hw_queue { + struct hl_hw_sob hw_sob[HL_RSVD_SOBS]; + struct hl_cs_job **shadow_queue; + enum hl_queue_type queue_type; + u64 kernel_address; + dma_addr_t bus_address; + u32 pi; + atomic_t ci; + u32 hw_queue_id; + u32 cq_id; + u32 msi_vec; + u16 int_queue_len; + u16 next_sob_val; + u16 base_sob_id; + u16 base_mon_id; + u8 valid; + u8 curr_sob_offset; + u8 supports_sync_stream; +}; + +/** + * struct hl_cq - describes a completion queue + * @hdev: pointer to the device structure + * @kernel_address: holds the queue's kernel virtual address + * @bus_address: holds the queue's DMA address + * @cq_idx: completion queue index in array + * @hw_queue_id: the id of the matching H/W queue + * @ci: ci inside the queue + * @pi: pi inside the queue + * @free_slots_cnt: counter of free slots in queue + */ +struct hl_cq { + struct hl_device *hdev; + u64 kernel_address; + dma_addr_t bus_address; + u32 cq_idx; + u32 hw_queue_id; + u32 ci; + u32 pi; + atomic_t free_slots_cnt; +}; + +/** + * struct hl_eq - describes the event queue (single one per device) + * @hdev: pointer to the device structure + * @kernel_address: holds the queue's kernel virtual address + * @bus_address: holds the queue's DMA address + * @ci: ci inside the queue + */ +struct hl_eq { + struct hl_device *hdev; + u64 kernel_address; + dma_addr_t bus_address; + u32 ci; +}; + + +/* + * ASICs + */ + +/** + * enum hl_asic_type - supported ASIC types. + * @ASIC_INVALID: Invalid ASIC type. + * @ASIC_GOYA: Goya device. + * @ASIC_GAUDI: Gaudi device. + */ +enum hl_asic_type { + ASIC_INVALID, + ASIC_GOYA, + ASIC_GAUDI +}; + +struct hl_cs_parser; + +/** + * enum hl_pm_mng_profile - power management profile. + * @PM_AUTO: internal clock is set by the Linux driver. + * @PM_MANUAL: internal clock is set by the user. + * @PM_LAST: last power management type. + */ +enum hl_pm_mng_profile { + PM_AUTO = 1, + PM_MANUAL, + PM_LAST +}; + +/** + * enum hl_pll_frequency - PLL frequency. + * @PLL_HIGH: high frequency. + * @PLL_LOW: low frequency. + * @PLL_LAST: last frequency values that were configured by the user. + */ +enum hl_pll_frequency { + PLL_HIGH = 1, + PLL_LOW, + PLL_LAST +}; + +#define PLL_REF_CLK 50 + +enum div_select_defs { + DIV_SEL_REF_CLK = 0, + DIV_SEL_PLL_CLK = 1, + DIV_SEL_DIVIDED_REF = 2, + DIV_SEL_DIVIDED_PLL = 3, +}; + +/** + * struct hl_asic_funcs - ASIC specific functions that are can be called from + * common code. + * @early_init: sets up early driver state (pre sw_init), doesn't configure H/W. + * @early_fini: tears down what was done in early_init. + * @late_init: sets up late driver/hw state (post hw_init) - Optional. + * @late_fini: tears down what was done in late_init (pre hw_fini) - Optional. + * @sw_init: sets up driver state, does not configure H/W. + * @sw_fini: tears down driver state, does not configure H/W. + * @hw_init: sets up the H/W state. + * @hw_fini: tears down the H/W state. + * @halt_engines: halt engines, needed for reset sequence. This also disables + * interrupts from the device. Should be called before + * hw_fini and before CS rollback. + * @suspend: handles IP specific H/W or SW changes for suspend. + * @resume: handles IP specific H/W or SW changes for resume. + * @cb_mmap: maps a CB. + * @ring_doorbell: increment PI on a given QMAN. + * @pqe_write: Write the PQ entry to the PQ. This is ASIC-specific + * function because the PQs are located in different memory areas + * per ASIC (SRAM, DRAM, Host memory) and therefore, the method of + * writing the PQE must match the destination memory area + * properties. + * @asic_dma_alloc_coherent: Allocate coherent DMA memory by calling + * dma_alloc_coherent(). This is ASIC function because + * its implementation is not trivial when the driver + * is loaded in simulation mode (not upstreamed). + * @asic_dma_free_coherent: Free coherent DMA memory by calling + * dma_free_coherent(). This is ASIC function because + * its implementation is not trivial when the driver + * is loaded in simulation mode (not upstreamed). + * @get_int_queue_base: get the internal queue base address. + * @test_queues: run simple test on all queues for sanity check. + * @asic_dma_pool_zalloc: small DMA allocation of coherent memory from DMA pool. + * size of allocation is HL_DMA_POOL_BLK_SIZE. + * @asic_dma_pool_free: free small DMA allocation from pool. + * @cpu_accessible_dma_pool_alloc: allocate CPU PQ packet from DMA pool. + * @cpu_accessible_dma_pool_free: free CPU PQ packet from DMA pool. + * @hl_dma_unmap_sg: DMA unmap scatter-gather list. + * @cs_parser: parse Command Submission. + * @asic_dma_map_sg: DMA map scatter-gather list. + * @get_dma_desc_list_size: get number of LIN_DMA packets required for CB. + * @add_end_of_cb_packets: Add packets to the end of CB, if device requires it. + * @update_eq_ci: update event queue CI. + * @context_switch: called upon ASID context switch. + * @restore_phase_topology: clear all SOBs amd MONs. + * @debugfs_read32: debug interface for reading u32 from DRAM/SRAM. + * @debugfs_write32: debug interface for writing u32 to DRAM/SRAM. + * @add_device_attr: add ASIC specific device attributes. + * @handle_eqe: handle event queue entry (IRQ) from ArmCP. + * @set_pll_profile: change PLL profile (manual/automatic). + * @get_events_stat: retrieve event queue entries histogram. + * @read_pte: read MMU page table entry from DRAM. + * @write_pte: write MMU page table entry to DRAM. + * @mmu_invalidate_cache: flush MMU STLB host/DRAM cache, either with soft + * (L1 only) or hard (L0 & L1) flush. + * @mmu_invalidate_cache_range: flush specific MMU STLB cache lines with + * ASID-VA-size mask. + * @send_heartbeat: send is-alive packet to ArmCP and verify response. + * @enable_clock_gating: enable clock gating for reducing power consumption. + * @disable_clock_gating: disable clock for accessing registers on HBW. + * @debug_coresight: perform certain actions on Coresight for debugging. + * @is_device_idle: return true if device is idle, false otherwise. + * @soft_reset_late_init: perform certain actions needed after soft reset. + * @hw_queues_lock: acquire H/W queues lock. + * @hw_queues_unlock: release H/W queues lock. + * @get_pci_id: retrieve PCI ID. + * @get_eeprom_data: retrieve EEPROM data from F/W. + * @send_cpu_message: send buffer to ArmCP. + * @get_hw_state: retrieve the H/W state + * @pci_bars_map: Map PCI BARs. + * @set_dram_bar_base: Set DRAM BAR to map specific device address. Returns + * old address the bar pointed to or U64_MAX for failure + * @init_iatu: Initialize the iATU unit inside the PCI controller. + * @rreg: Read a register. Needed for simulator support. + * @wreg: Write a register. Needed for simulator support. + * @halt_coresight: stop the ETF and ETR traces. + * @get_clk_rate: Retrieve the ASIC current and maximum clock rate in MHz + * @get_queue_id_for_cq: Get the H/W queue id related to the given CQ index. + * @read_device_fw_version: read the device's firmware versions that are + * contained in registers + * @load_firmware_to_device: load the firmware to the device's memory + * @load_boot_fit_to_device: load boot fit to device's memory + * @get_signal_cb_size: Get signal CB size. + * @get_wait_cb_size: Get wait CB size. + * @gen_signal_cb: Generate a signal CB. + * @gen_wait_cb: Generate a wait CB. + * @reset_sob: Reset a SOB. + * @set_dma_mask_from_fw: set the DMA mask in the driver according to the + * firmware configuration + * @get_device_time: Get the device time. + */ +struct hl_asic_funcs { + int (*early_init)(struct hl_device *hdev); + int (*early_fini)(struct hl_device *hdev); + int (*late_init)(struct hl_device *hdev); + void (*late_fini)(struct hl_device *hdev); + int (*sw_init)(struct hl_device *hdev); + int (*sw_fini)(struct hl_device *hdev); + int (*hw_init)(struct hl_device *hdev); + void (*hw_fini)(struct hl_device *hdev, bool hard_reset); + void (*halt_engines)(struct hl_device *hdev, bool hard_reset); + int (*suspend)(struct hl_device *hdev); + int (*resume)(struct hl_device *hdev); + int (*cb_mmap)(struct hl_device *hdev, struct vm_area_struct *vma, + u64 kaddress, phys_addr_t paddress, u32 size); + void (*ring_doorbell)(struct hl_device *hdev, u32 hw_queue_id, u32 pi); + void (*pqe_write)(struct hl_device *hdev, __le64 *pqe, + struct hl_bd *bd); + void* (*asic_dma_alloc_coherent)(struct hl_device *hdev, size_t size, + dma_addr_t *dma_handle, gfp_t flag); + void (*asic_dma_free_coherent)(struct hl_device *hdev, size_t size, + void *cpu_addr, dma_addr_t dma_handle); + void* (*get_int_queue_base)(struct hl_device *hdev, u32 queue_id, + dma_addr_t *dma_handle, u16 *queue_len); + int (*test_queues)(struct hl_device *hdev); + void* (*asic_dma_pool_zalloc)(struct hl_device *hdev, size_t size, + gfp_t mem_flags, dma_addr_t *dma_handle); + void (*asic_dma_pool_free)(struct hl_device *hdev, void *vaddr, + dma_addr_t dma_addr); + void* (*cpu_accessible_dma_pool_alloc)(struct hl_device *hdev, + size_t size, dma_addr_t *dma_handle); + void (*cpu_accessible_dma_pool_free)(struct hl_device *hdev, + size_t size, void *vaddr); + void (*hl_dma_unmap_sg)(struct hl_device *hdev, + struct scatterlist *sgl, int nents, + enum dma_data_direction dir); + int (*cs_parser)(struct hl_device *hdev, struct hl_cs_parser *parser); + int (*asic_dma_map_sg)(struct hl_device *hdev, + struct scatterlist *sgl, int nents, + enum dma_data_direction dir); + u32 (*get_dma_desc_list_size)(struct hl_device *hdev, + struct sg_table *sgt); + void (*add_end_of_cb_packets)(struct hl_device *hdev, + u64 kernel_address, u32 len, + u64 cq_addr, u32 cq_val, u32 msix_num, + bool eb); + void (*update_eq_ci)(struct hl_device *hdev, u32 val); + int (*context_switch)(struct hl_device *hdev, u32 asid); + void (*restore_phase_topology)(struct hl_device *hdev); + int (*debugfs_read32)(struct hl_device *hdev, u64 addr, u32 *val); + int (*debugfs_write32)(struct hl_device *hdev, u64 addr, u32 val); + int (*debugfs_read64)(struct hl_device *hdev, u64 addr, u64 *val); + int (*debugfs_write64)(struct hl_device *hdev, u64 addr, u64 val); + void (*add_device_attr)(struct hl_device *hdev, + struct attribute_group *dev_attr_grp); + void (*handle_eqe)(struct hl_device *hdev, + struct hl_eq_entry *eq_entry); + void (*set_pll_profile)(struct hl_device *hdev, + enum hl_pll_frequency freq); + void* (*get_events_stat)(struct hl_device *hdev, bool aggregate, + u32 *size); + u64 (*read_pte)(struct hl_device *hdev, u64 addr); + void (*write_pte)(struct hl_device *hdev, u64 addr, u64 val); + int (*mmu_invalidate_cache)(struct hl_device *hdev, bool is_hard, + u32 flags); + int (*mmu_invalidate_cache_range)(struct hl_device *hdev, bool is_hard, + u32 asid, u64 va, u64 size); + int (*send_heartbeat)(struct hl_device *hdev); + void (*enable_clock_gating)(struct hl_device *hdev); + void (*disable_clock_gating)(struct hl_device *hdev); + int (*debug_coresight)(struct hl_device *hdev, void *data); + bool (*is_device_idle)(struct hl_device *hdev, u32 *mask, + struct seq_file *s); + int (*soft_reset_late_init)(struct hl_device *hdev); + void (*hw_queues_lock)(struct hl_device *hdev); + void (*hw_queues_unlock)(struct hl_device *hdev); + u32 (*get_pci_id)(struct hl_device *hdev); + int (*get_eeprom_data)(struct hl_device *hdev, void *data, + size_t max_size); + int (*send_cpu_message)(struct hl_device *hdev, u32 *msg, + u16 len, u32 timeout, long *result); + enum hl_device_hw_state (*get_hw_state)(struct hl_device *hdev); + int (*pci_bars_map)(struct hl_device *hdev); + u64 (*set_dram_bar_base)(struct hl_device *hdev, u64 addr); + int (*init_iatu)(struct hl_device *hdev); + u32 (*rreg)(struct hl_device *hdev, u32 reg); + void (*wreg)(struct hl_device *hdev, u32 reg, u32 val); + void (*halt_coresight)(struct hl_device *hdev); + int (*get_clk_rate)(struct hl_device *hdev, u32 *cur_clk, u32 *max_clk); + u32 (*get_queue_id_for_cq)(struct hl_device *hdev, u32 cq_idx); + void (*read_device_fw_version)(struct hl_device *hdev, + enum hl_fw_component fwc); + int (*load_firmware_to_device)(struct hl_device *hdev); + int (*load_boot_fit_to_device)(struct hl_device *hdev); + u32 (*get_signal_cb_size)(struct hl_device *hdev); + u32 (*get_wait_cb_size)(struct hl_device *hdev); + void (*gen_signal_cb)(struct hl_device *hdev, void *data, u16 sob_id); + void (*gen_wait_cb)(struct hl_device *hdev, void *data, u16 sob_id, + u16 sob_val, u16 mon_id, u32 q_idx); + void (*reset_sob)(struct hl_device *hdev, void *data); + void (*set_dma_mask_from_fw)(struct hl_device *hdev); + u64 (*get_device_time)(struct hl_device *hdev); +}; + + +/* + * CONTEXTS + */ + +#define HL_KERNEL_ASID_ID 0 + +/** + * struct hl_va_range - virtual addresses range. + * @lock: protects the virtual addresses list. + * @list: list of virtual addresses blocks available for mappings. + * @start_addr: range start address. + * @end_addr: range end address. + */ +struct hl_va_range { + struct mutex lock; + struct list_head list; + u64 start_addr; + u64 end_addr; +}; + +/** + * struct hl_ctx - user/kernel context. + * @mem_hash: holds mapping from virtual address to virtual memory area + * descriptor (hl_vm_phys_pg_list or hl_userptr). + * @mmu_shadow_hash: holds a mapping from shadow address to pgt_info structure. + * @hpriv: pointer to the private (Kernel Driver) data of the process (fd). + * @hdev: pointer to the device structure. + * @refcount: reference counter for the context. Context is released only when + * this hits 0l. It is incremented on CS and CS_WAIT. + * @cs_pending: array of DMA fence objects representing pending CS. + * @host_va_range: holds available virtual addresses for host mappings. + * @host_huge_va_range: holds available virtual addresses for host mappings + * with huge pages. + * @dram_va_range: holds available virtual addresses for DRAM mappings. + * @mem_hash_lock: protects the mem_hash. + * @mmu_lock: protects the MMU page tables. Any change to the PGT, modifying the + * MMU hash or walking the PGT requires talking this lock. + * @debugfs_list: node in debugfs list of contexts. + * @cs_sequence: sequence number for CS. Value is assigned to a CS and passed + * to user so user could inquire about CS. It is used as + * index to cs_pending array. + * @dram_default_hops: array that holds all hops addresses needed for default + * DRAM mapping. + * @cs_lock: spinlock to protect cs_sequence. + * @dram_phys_mem: amount of used physical DRAM memory by this context. + * @thread_ctx_switch_token: token to prevent multiple threads of the same + * context from running the context switch phase. + * Only a single thread should run it. + * @thread_ctx_switch_wait_token: token to prevent the threads that didn't run + * the context switch phase from moving to their + * execution phase before the context switch phase + * has finished. + * @asid: context's unique address space ID in the device's MMU. + * @handle: context's opaque handle for user + */ +struct hl_ctx { + DECLARE_HASHTABLE(mem_hash, MEM_HASH_TABLE_BITS); + DECLARE_HASHTABLE(mmu_shadow_hash, MMU_HASH_TABLE_BITS); + struct hl_fpriv *hpriv; + struct hl_device *hdev; + struct kref refcount; + struct dma_fence **cs_pending; + struct hl_va_range *host_va_range; + struct hl_va_range *host_huge_va_range; + struct hl_va_range *dram_va_range; + struct mutex mem_hash_lock; + struct mutex mmu_lock; + struct list_head debugfs_list; + struct hl_cs_counters cs_counters; + u64 cs_sequence; + u64 *dram_default_hops; + spinlock_t cs_lock; + atomic64_t dram_phys_mem; + atomic_t thread_ctx_switch_token; + u32 thread_ctx_switch_wait_token; + u32 asid; + u32 handle; +}; + +/** + * struct hl_ctx_mgr - for handling multiple contexts. + * @ctx_lock: protects ctx_handles. + * @ctx_handles: idr to hold all ctx handles. + */ +struct hl_ctx_mgr { + struct mutex ctx_lock; + struct idr ctx_handles; +}; + + + +/* + * COMMAND SUBMISSIONS + */ + +/** + * struct hl_userptr - memory mapping chunk information + * @vm_type: type of the VM. + * @job_node: linked-list node for hanging the object on the Job's list. + * @vec: pointer to the frame vector. + * @sgt: pointer to the scatter-gather table that holds the pages. + * @dir: for DMA unmapping, the direction must be supplied, so save it. + * @debugfs_list: node in debugfs list of command submissions. + * @addr: user-space virtual address of the start of the memory area. + * @size: size of the memory area to pin & map. + * @dma_mapped: true if the SG was mapped to DMA addresses, false otherwise. + */ +struct hl_userptr { + enum vm_type_t vm_type; /* must be first */ + struct list_head job_node; + struct frame_vector *vec; + struct sg_table *sgt; + enum dma_data_direction dir; + struct list_head debugfs_list; + u64 addr; + u32 size; + u8 dma_mapped; +}; + +/** + * struct hl_cs - command submission. + * @jobs_in_queue_cnt: per each queue, maintain counter of submitted jobs. + * @ctx: the context this CS belongs to. + * @job_list: list of the CS's jobs in the various queues. + * @job_lock: spinlock for the CS's jobs list. Needed for free_job. + * @refcount: reference counter for usage of the CS. + * @fence: pointer to the fence object of this CS. + * @signal_fence: pointer to the fence object of the signal CS (used by wait + * CS only). + * @finish_work: workqueue object to run when CS is completed by H/W. + * @work_tdr: delayed work node for TDR. + * @mirror_node : node in device mirror list of command submissions. + * @debugfs_list: node in debugfs list of command submissions. + * @sequence: the sequence number of this CS. + * @type: CS_TYPE_*. + * @submitted: true if CS was submitted to H/W. + * @completed: true if CS was completed by device. + * @timedout : true if CS was timedout. + * @tdr_active: true if TDR was activated for this CS (to prevent + * double TDR activation). + * @aborted: true if CS was aborted due to some device error. + */ +struct hl_cs { + u16 *jobs_in_queue_cnt; + struct hl_ctx *ctx; + struct list_head job_list; + spinlock_t job_lock; + struct kref refcount; + struct dma_fence *fence; + struct dma_fence *signal_fence; + struct work_struct finish_work; + struct delayed_work work_tdr; + struct list_head mirror_node; + struct list_head debugfs_list; + u64 sequence; + enum hl_cs_type type; + u8 submitted; + u8 completed; + u8 timedout; + u8 tdr_active; + u8 aborted; +}; + +/** + * struct hl_cs_job - command submission job. + * @cs_node: the node to hang on the CS jobs list. + * @cs: the CS this job belongs to. + * @user_cb: the CB we got from the user. + * @patched_cb: in case of patching, this is internal CB which is submitted on + * the queue instead of the CB we got from the IOCTL. + * @finish_work: workqueue object to run when job is completed. + * @userptr_list: linked-list of userptr mappings that belong to this job and + * wait for completion. + * @debugfs_list: node in debugfs list of command submission jobs. + * @queue_type: the type of the H/W queue this job is submitted to. + * @id: the id of this job inside a CS. + * @hw_queue_id: the id of the H/W queue this job is submitted to. + * @user_cb_size: the actual size of the CB we got from the user. + * @job_cb_size: the actual size of the CB that we put on the queue. + * @is_kernel_allocated_cb: true if the CB handle we got from the user holds a + * handle to a kernel-allocated CB object, false + * otherwise (SRAM/DRAM/host address). + * @contains_dma_pkt: whether the JOB contains at least one DMA packet. This + * info is needed later, when adding the 2xMSG_PROT at the + * end of the JOB, to know which barriers to put in the + * MSG_PROT packets. Relevant only for GAUDI as GOYA doesn't + * have streams so the engine can't be busy by another + * stream. + */ +struct hl_cs_job { + struct list_head cs_node; + struct hl_cs *cs; + struct hl_cb *user_cb; + struct hl_cb *patched_cb; + struct work_struct finish_work; + struct list_head userptr_list; + struct list_head debugfs_list; + enum hl_queue_type queue_type; + u32 id; + u32 hw_queue_id; + u32 user_cb_size; + u32 job_cb_size; + u8 is_kernel_allocated_cb; + u8 contains_dma_pkt; +}; + +/** + * struct hl_cs_parser - command submission parser properties. + * @user_cb: the CB we got from the user. + * @patched_cb: in case of patching, this is internal CB which is submitted on + * the queue instead of the CB we got from the IOCTL. + * @job_userptr_list: linked-list of userptr mappings that belong to the related + * job and wait for completion. + * @cs_sequence: the sequence number of the related CS. + * @queue_type: the type of the H/W queue this job is submitted to. + * @ctx_id: the ID of the context the related CS belongs to. + * @hw_queue_id: the id of the H/W queue this job is submitted to. + * @user_cb_size: the actual size of the CB we got from the user. + * @patched_cb_size: the size of the CB after parsing. + * @job_id: the id of the related job inside the related CS. + * @is_kernel_allocated_cb: true if the CB handle we got from the user holds a + * handle to a kernel-allocated CB object, false + * otherwise (SRAM/DRAM/host address). + * @contains_dma_pkt: whether the JOB contains at least one DMA packet. This + * info is needed later, when adding the 2xMSG_PROT at the + * end of the JOB, to know which barriers to put in the + * MSG_PROT packets. Relevant only for GAUDI as GOYA doesn't + * have streams so the engine can't be busy by another + * stream. + */ +struct hl_cs_parser { + struct hl_cb *user_cb; + struct hl_cb *patched_cb; + struct list_head *job_userptr_list; + u64 cs_sequence; + enum hl_queue_type queue_type; + u32 ctx_id; + u32 hw_queue_id; + u32 user_cb_size; + u32 patched_cb_size; + u8 job_id; + u8 is_kernel_allocated_cb; + u8 contains_dma_pkt; +}; + + +/* + * MEMORY STRUCTURE + */ + +/** + * struct hl_vm_hash_node - hash element from virtual address to virtual + * memory area descriptor (hl_vm_phys_pg_list or + * hl_userptr). + * @node: node to hang on the hash table in context object. + * @vaddr: key virtual address. + * @ptr: value pointer (hl_vm_phys_pg_list or hl_userptr). + */ +struct hl_vm_hash_node { + struct hlist_node node; + u64 vaddr; + void *ptr; +}; + +/** + * struct hl_vm_phys_pg_pack - physical page pack. + * @vm_type: describes the type of the virtual area descriptor. + * @pages: the physical page array. + * @npages: num physical pages in the pack. + * @total_size: total size of all the pages in this list. + * @mapping_cnt: number of shared mappings. + * @asid: the context related to this list. + * @page_size: size of each page in the pack. + * @flags: HL_MEM_* flags related to this list. + * @handle: the provided handle related to this list. + * @offset: offset from the first page. + * @contiguous: is contiguous physical memory. + * @created_from_userptr: is product of host virtual address. + */ +struct hl_vm_phys_pg_pack { + enum vm_type_t vm_type; /* must be first */ + u64 *pages; + u64 npages; + u64 total_size; + atomic_t mapping_cnt; + u32 asid; + u32 page_size; + u32 flags; + u32 handle; + u32 offset; + u8 contiguous; + u8 created_from_userptr; +}; + +/** + * struct hl_vm_va_block - virtual range block information. + * @node: node to hang on the virtual range list in context object. + * @start: virtual range start address. + * @end: virtual range end address. + * @size: virtual range size. + */ +struct hl_vm_va_block { + struct list_head node; + u64 start; + u64 end; + u64 size; +}; + +/** + * struct hl_vm - virtual memory manager for MMU. + * @dram_pg_pool: pool for DRAM physical pages of 2MB. + * @dram_pg_pool_refcount: reference counter for the pool usage. + * @idr_lock: protects the phys_pg_list_handles. + * @phys_pg_pack_handles: idr to hold all device allocations handles. + * @init_done: whether initialization was done. We need this because VM + * initialization might be skipped during device initialization. + */ +struct hl_vm { + struct gen_pool *dram_pg_pool; + struct kref dram_pg_pool_refcount; + spinlock_t idr_lock; + struct idr phys_pg_pack_handles; + u8 init_done; +}; + + +/* + * DEBUG, PROFILING STRUCTURE + */ + +/** + * struct hl_debug_params - Coresight debug parameters. + * @input: pointer to component specific input parameters. + * @output: pointer to component specific output parameters. + * @output_size: size of output buffer. + * @reg_idx: relevant register ID. + * @op: component operation to execute. + * @enable: true if to enable component debugging, false otherwise. + */ +struct hl_debug_params { + void *input; + void *output; + u32 output_size; + u32 reg_idx; + u32 op; + bool enable; +}; + +/* + * FILE PRIVATE STRUCTURE + */ + +/** + * struct hl_fpriv - process information stored in FD private data. + * @hdev: habanalabs device structure. + * @filp: pointer to the given file structure. + * @taskpid: current process ID. + * @ctx: current executing context. TODO: remove for multiple ctx per process + * @ctx_mgr: context manager to handle multiple context for this FD. + * @cb_mgr: command buffer manager to handle multiple buffers for this FD. + * @debugfs_list: list of relevant ASIC debugfs. + * @dev_node: node in the device list of file private data + * @refcount: number of related contexts. + * @restore_phase_mutex: lock for context switch and restore phase. + * @is_control: true for control device, false otherwise + */ +struct hl_fpriv { + struct hl_device *hdev; + struct file *filp; + struct pid *taskpid; + struct hl_ctx *ctx; + struct hl_ctx_mgr ctx_mgr; + struct hl_cb_mgr cb_mgr; + struct list_head debugfs_list; + struct list_head dev_node; + struct kref refcount; + struct mutex restore_phase_mutex; + u8 is_control; +}; + + +/* + * DebugFS + */ + +/** + * struct hl_info_list - debugfs file ops. + * @name: file name. + * @show: function to output information. + * @write: function to write to the file. + */ +struct hl_info_list { + const char *name; + int (*show)(struct seq_file *s, void *data); + ssize_t (*write)(struct file *file, const char __user *buf, + size_t count, loff_t *f_pos); +}; + +/** + * struct hl_debugfs_entry - debugfs dentry wrapper. + * @dent: base debugfs entry structure. + * @info_ent: dentry realted ops. + * @dev_entry: ASIC specific debugfs manager. + */ +struct hl_debugfs_entry { + struct dentry *dent; + const struct hl_info_list *info_ent; + struct hl_dbg_device_entry *dev_entry; +}; + +/** + * struct hl_dbg_device_entry - ASIC specific debugfs manager. + * @root: root dentry. + * @hdev: habanalabs device structure. + * @entry_arr: array of available hl_debugfs_entry. + * @file_list: list of available debugfs files. + * @file_mutex: protects file_list. + * @cb_list: list of available CBs. + * @cb_spinlock: protects cb_list. + * @cs_list: list of available CSs. + * @cs_spinlock: protects cs_list. + * @cs_job_list: list of available CB jobs. + * @cs_job_spinlock: protects cs_job_list. + * @userptr_list: list of available userptrs (virtual memory chunk descriptor). + * @userptr_spinlock: protects userptr_list. + * @ctx_mem_hash_list: list of available contexts with MMU mappings. + * @ctx_mem_hash_spinlock: protects cb_list. + * @addr: next address to read/write from/to in read/write32. + * @mmu_addr: next virtual address to translate to physical address in mmu_show. + * @mmu_asid: ASID to use while translating in mmu_show. + * @i2c_bus: generic u8 debugfs file for bus value to use in i2c_data_read. + * @i2c_bus: generic u8 debugfs file for address value to use in i2c_data_read. + * @i2c_bus: generic u8 debugfs file for register value to use in i2c_data_read. + */ +struct hl_dbg_device_entry { + struct dentry *root; + struct hl_device *hdev; + struct hl_debugfs_entry *entry_arr; + struct list_head file_list; + struct mutex file_mutex; + struct list_head cb_list; + spinlock_t cb_spinlock; + struct list_head cs_list; + spinlock_t cs_spinlock; + struct list_head cs_job_list; + spinlock_t cs_job_spinlock; + struct list_head userptr_list; + spinlock_t userptr_spinlock; + struct list_head ctx_mem_hash_list; + spinlock_t ctx_mem_hash_spinlock; + u64 addr; + u64 mmu_addr; + u32 mmu_asid; + u8 i2c_bus; + u8 i2c_addr; + u8 i2c_reg; +}; + + +/* + * DEVICES + */ + +/* Theoretical limit only. A single host can only contain up to 4 or 8 PCIe + * x16 cards. In extreme cases, there are hosts that can accommodate 16 cards. + */ +#define HL_MAX_MINORS 256 + +/* + * Registers read & write functions. + */ + +u32 hl_rreg(struct hl_device *hdev, u32 reg); +void hl_wreg(struct hl_device *hdev, u32 reg, u32 val); + +#define RREG32(reg) hdev->asic_funcs->rreg(hdev, (reg)) +#define WREG32(reg, v) hdev->asic_funcs->wreg(hdev, (reg), (v)) +#define DREG32(reg) pr_info("REGISTER: " #reg " : 0x%08X\n", \ + hdev->asic_funcs->rreg(hdev, (reg))) + +#define WREG32_P(reg, val, mask) \ + do { \ + u32 tmp_ = RREG32(reg); \ + tmp_ &= (mask); \ + tmp_ |= ((val) & ~(mask)); \ + WREG32(reg, tmp_); \ + } while (0) +#define WREG32_AND(reg, and) WREG32_P(reg, 0, and) +#define WREG32_OR(reg, or) WREG32_P(reg, or, ~(or)) + +#define RMWREG32(reg, val, mask) \ + do { \ + u32 tmp_ = RREG32(reg); \ + tmp_ &= ~(mask); \ + tmp_ |= ((val) << __ffs(mask)); \ + WREG32(reg, tmp_); \ + } while (0) + +#define RREG32_MASK(reg, mask) ((RREG32(reg) & mask) >> __ffs(mask)) + +#define REG_FIELD_SHIFT(reg, field) reg##_##field##_SHIFT +#define REG_FIELD_MASK(reg, field) reg##_##field##_MASK +#define WREG32_FIELD(reg, offset, field, val) \ + WREG32(mm##reg + offset, (RREG32(mm##reg + offset) & \ + ~REG_FIELD_MASK(reg, field)) | \ + (val) << REG_FIELD_SHIFT(reg, field)) + +/* Timeout should be longer when working with simulator but cap the + * increased timeout to some maximum + */ +#define hl_poll_timeout(hdev, addr, val, cond, sleep_us, timeout_us) \ +({ \ + ktime_t __timeout; \ + if (hdev->pdev) \ + __timeout = ktime_add_us(ktime_get(), timeout_us); \ + else \ + __timeout = ktime_add_us(ktime_get(),\ + min((u64)(timeout_us * 10), \ + (u64) HL_SIM_MAX_TIMEOUT_US)); \ + might_sleep_if(sleep_us); \ + for (;;) { \ + (val) = RREG32(addr); \ + if (cond) \ + break; \ + if (timeout_us && ktime_compare(ktime_get(), __timeout) > 0) { \ + (val) = RREG32(addr); \ + break; \ + } \ + if (sleep_us) \ + usleep_range((sleep_us >> 2) + 1, sleep_us); \ + } \ + (cond) ? 0 : -ETIMEDOUT; \ +}) + +/* + * address in this macro points always to a memory location in the + * host's (server's) memory. That location is updated asynchronously + * either by the direct access of the device or by another core. + * + * To work both in LE and BE architectures, we need to distinguish between the + * two states (device or another core updates the memory location). Therefore, + * if mem_written_by_device is true, the host memory being polled will be + * updated directly by the device. If false, the host memory being polled will + * be updated by host CPU. Required so host knows whether or not the memory + * might need to be byte-swapped before returning value to caller. + */ +#define hl_poll_timeout_memory(hdev, addr, val, cond, sleep_us, timeout_us, \ + mem_written_by_device) \ +({ \ + ktime_t __timeout; \ + if (hdev->pdev) \ + __timeout = ktime_add_us(ktime_get(), timeout_us); \ + else \ + __timeout = ktime_add_us(ktime_get(),\ + min((u64)(timeout_us * 10), \ + (u64) HL_SIM_MAX_TIMEOUT_US)); \ + might_sleep_if(sleep_us); \ + for (;;) { \ + /* Verify we read updates done by other cores or by device */ \ + mb(); \ + (val) = *((u32 *) (uintptr_t) (addr)); \ + if (mem_written_by_device) \ + (val) = le32_to_cpu(*(__le32 *) &(val)); \ + if (cond) \ + break; \ + if (timeout_us && ktime_compare(ktime_get(), __timeout) > 0) { \ + (val) = *((u32 *) (uintptr_t) (addr)); \ + if (mem_written_by_device) \ + (val) = le32_to_cpu(*(__le32 *) &(val)); \ + break; \ + } \ + if (sleep_us) \ + usleep_range((sleep_us >> 2) + 1, sleep_us); \ + } \ + (cond) ? 0 : -ETIMEDOUT; \ +}) + +#define hl_poll_timeout_device_memory(hdev, addr, val, cond, sleep_us, \ + timeout_us) \ +({ \ + ktime_t __timeout; \ + if (hdev->pdev) \ + __timeout = ktime_add_us(ktime_get(), timeout_us); \ + else \ + __timeout = ktime_add_us(ktime_get(),\ + min((u64)(timeout_us * 10), \ + (u64) HL_SIM_MAX_TIMEOUT_US)); \ + might_sleep_if(sleep_us); \ + for (;;) { \ + (val) = readl(addr); \ + if (cond) \ + break; \ + if (timeout_us && ktime_compare(ktime_get(), __timeout) > 0) { \ + (val) = readl(addr); \ + break; \ + } \ + if (sleep_us) \ + usleep_range((sleep_us >> 2) + 1, sleep_us); \ + } \ + (cond) ? 0 : -ETIMEDOUT; \ +}) + +struct hwmon_chip_info; + +/** + * struct hl_device_reset_work - reset workqueue task wrapper. + * @reset_work: reset work to be done. + * @hdev: habanalabs device structure. + */ +struct hl_device_reset_work { + struct work_struct reset_work; + struct hl_device *hdev; +}; + +/** + * struct hl_device_idle_busy_ts - used for calculating device utilization rate. + * @idle_to_busy_ts: timestamp where device changed from idle to busy. + * @busy_to_idle_ts: timestamp where device changed from busy to idle. + */ +struct hl_device_idle_busy_ts { + ktime_t idle_to_busy_ts; + ktime_t busy_to_idle_ts; +}; + +/** + * struct hl_device - habanalabs device structure. + * @pdev: pointer to PCI device, can be NULL in case of simulator device. + * @pcie_bar_phys: array of available PCIe bars physical addresses. + * (required only for PCI address match mode) + * @pcie_bar: array of available PCIe bars virtual addresses. + * @rmmio: configuration area address on SRAM. + * @cdev: related char device. + * @cdev_ctrl: char device for control operations only (INFO IOCTL) + * @dev: related kernel basic device structure. + * @dev_ctrl: related kernel device structure for the control device + * @work_freq: delayed work to lower device frequency if possible. + * @work_heartbeat: delayed work for ArmCP is-alive check. + * @asic_name: ASIC specific nmae. + * @asic_type: ASIC specific type. + * @completion_queue: array of hl_cq. + * @cq_wq: work queues of completion queues for executing work in process + * context. + * @eq_wq: work queue of event queue for executing work in process context. + * @kernel_ctx: Kernel driver context structure. + * @kernel_queues: array of hl_hw_queue. + * @hw_queues_mirror_list: CS mirror list for TDR. + * @hw_queues_mirror_lock: protects hw_queues_mirror_list. + * @kernel_cb_mgr: command buffer manager for creating/destroying/handling CGs. + * @event_queue: event queue for IRQ from ArmCP. + * @dma_pool: DMA pool for small allocations. + * @cpu_accessible_dma_mem: Host <-> ArmCP shared memory CPU address. + * @cpu_accessible_dma_address: Host <-> ArmCP shared memory DMA address. + * @cpu_accessible_dma_pool: Host <-> ArmCP shared memory pool. + * @asid_bitmap: holds used/available ASIDs. + * @asid_mutex: protects asid_bitmap. + * @send_cpu_message_lock: enforces only one message in Host <-> ArmCP queue. + * @debug_lock: protects critical section of setting debug mode for device + * @asic_prop: ASIC specific immutable properties. + * @asic_funcs: ASIC specific functions. + * @asic_specific: ASIC specific information to use only from ASIC files. + * @mmu_pgt_pool: pool of available MMU hops. + * @vm: virtual memory manager for MMU. + * @mmu_cache_lock: protects MMU cache invalidation as it can serve one context. + * @mmu_shadow_hop0: shadow mapping of the MMU hop 0 zone. + * @hwmon_dev: H/W monitor device. + * @pm_mng_profile: current power management profile. + * @hl_chip_info: ASIC's sensors information. + * @hl_debugfs: device's debugfs manager. + * @cb_pool: list of preallocated CBs. + * @cb_pool_lock: protects the CB pool. + * @fpriv_list: list of file private data structures. Each structure is created + * when a user opens the device + * @fpriv_list_lock: protects the fpriv_list + * @compute_ctx: current compute context executing. + * @idle_busy_ts_arr: array to hold time stamps of transitions from idle to busy + * and vice-versa + * @aggregated_cs_counters: aggregated cs counters among all contexts + * @dram_used_mem: current DRAM memory consumption. + * @timeout_jiffies: device CS timeout value. + * @max_power: the max power of the device, as configured by the sysadmin. This + * value is saved so in case of hard-reset, the driver will restore + * this value and update the F/W after the re-initialization + * @in_reset: is device in reset flow. + * @curr_pll_profile: current PLL profile. + * @cs_active_cnt: number of active command submissions on this device (active + * means already in H/W queues) + * @major: habanalabs kernel driver major. + * @high_pll: high PLL profile frequency. + * @soft_reset_cnt: number of soft reset since the driver was loaded. + * @hard_reset_cnt: number of hard reset since the driver was loaded. + * @idle_busy_ts_idx: index of current entry in idle_busy_ts_arr + * @id: device minor. + * @id_control: minor of the control device + * @cpu_pci_msb_addr: 50-bit extension bits for the device CPU's 40-bit + * addresses. + * @disabled: is device disabled. + * @late_init_done: is late init stage was done during initialization. + * @hwmon_initialized: is H/W monitor sensors was initialized. + * @hard_reset_pending: is there a hard reset work pending. + * @heartbeat: is heartbeat sanity check towards ArmCP enabled. + * @reset_on_lockup: true if a reset should be done in case of stuck CS, false + * otherwise. + * @dram_supports_virtual_memory: is MMU enabled towards DRAM. + * @dram_default_page_mapping: is DRAM default page mapping enabled. + * @pmmu_huge_range: is a different virtual addresses range used for PMMU with + * huge pages. + * @init_done: is the initialization of the device done. + * @mmu_enable: is MMU enabled. + * @mmu_huge_page_opt: is MMU huge pages optimization enabled. + * @clock_gating: is clock gating enabled. + * @device_cpu_disabled: is the device CPU disabled (due to timeouts) + * @dma_mask: the dma mask that was set for this device + * @in_debug: is device under debug. This, together with fpriv_list, enforces + * that only a single user is configuring the debug infrastructure. + * @power9_64bit_dma_enable: true to enable 64-bit DMA mask support. Relevant + * only to POWER9 machines. + * @cdev_sysfs_created: were char devices and sysfs nodes created. + * @stop_on_err: true if engines should stop on error. + * @supports_sync_stream: is sync stream supported. + * @sync_stream_queue_idx: helper index for sync stream queues initialization. + * @supports_coresight: is CoreSight supported. + * @supports_soft_reset: is soft reset supported. + */ +struct hl_device { + struct pci_dev *pdev; + u64 pcie_bar_phys[HL_PCI_NUM_BARS]; + void __iomem *pcie_bar[HL_PCI_NUM_BARS]; + void __iomem *rmmio; + struct cdev cdev; + struct cdev cdev_ctrl; + struct device *dev; + struct device *dev_ctrl; + struct delayed_work work_freq; + struct delayed_work work_heartbeat; + char asic_name[16]; + enum hl_asic_type asic_type; + struct hl_cq *completion_queue; + struct workqueue_struct **cq_wq; + struct workqueue_struct *eq_wq; + struct hl_ctx *kernel_ctx; + struct hl_hw_queue *kernel_queues; + struct list_head hw_queues_mirror_list; + spinlock_t hw_queues_mirror_lock; + struct hl_cb_mgr kernel_cb_mgr; + struct hl_eq event_queue; + struct dma_pool *dma_pool; + void *cpu_accessible_dma_mem; + dma_addr_t cpu_accessible_dma_address; + struct gen_pool *cpu_accessible_dma_pool; + unsigned long *asid_bitmap; + struct mutex asid_mutex; + struct mutex send_cpu_message_lock; + struct mutex debug_lock; + struct asic_fixed_properties asic_prop; + const struct hl_asic_funcs *asic_funcs; + void *asic_specific; + struct gen_pool *mmu_pgt_pool; + struct hl_vm vm; + struct mutex mmu_cache_lock; + void *mmu_shadow_hop0; + struct device *hwmon_dev; + enum hl_pm_mng_profile pm_mng_profile; + struct hwmon_chip_info *hl_chip_info; + + struct hl_dbg_device_entry hl_debugfs; + + struct list_head cb_pool; + spinlock_t cb_pool_lock; + + struct list_head fpriv_list; + struct mutex fpriv_list_lock; + + struct hl_ctx *compute_ctx; + + struct hl_device_idle_busy_ts *idle_busy_ts_arr; + + struct hl_cs_counters aggregated_cs_counters; + + atomic64_t dram_used_mem; + u64 timeout_jiffies; + u64 max_power; + atomic_t in_reset; + enum hl_pll_frequency curr_pll_profile; + int cs_active_cnt; + u32 major; + u32 high_pll; + u32 soft_reset_cnt; + u32 hard_reset_cnt; + u32 idle_busy_ts_idx; + u16 id; + u16 id_control; + u16 cpu_pci_msb_addr; + u8 disabled; + u8 late_init_done; + u8 hwmon_initialized; + u8 hard_reset_pending; + u8 heartbeat; + u8 reset_on_lockup; + u8 dram_supports_virtual_memory; + u8 dram_default_page_mapping; + u8 pmmu_huge_range; + u8 init_done; + u8 clock_gating; + u8 device_cpu_disabled; + u8 dma_mask; + u8 in_debug; + u8 power9_64bit_dma_enable; + u8 cdev_sysfs_created; + u8 stop_on_err; + u8 supports_sync_stream; + u8 sync_stream_queue_idx; + u8 supports_coresight; + u8 supports_soft_reset; + + /* Parameters for bring-up */ + u8 mmu_enable; + u8 mmu_huge_page_opt; + u8 cpu_enable; + u8 reset_pcilink; + u8 cpu_queues_enable; + u8 fw_loading; + u8 pldm; + u8 axi_drain; + u8 sram_scrambler_enable; + u8 dram_scrambler_enable; + u8 hard_reset_on_fw_events; + u8 bmc_enable; + u8 rl_enable; +}; + + +/* + * IOCTLs + */ + +/** + * typedef hl_ioctl_t - typedef for ioctl function in the driver + * @hpriv: pointer to the FD's private data, which contains state of + * user process + * @data: pointer to the input/output arguments structure of the IOCTL + * + * Return: 0 for success, negative value for error + */ +typedef int hl_ioctl_t(struct hl_fpriv *hpriv, void *data); + +/** + * struct hl_ioctl_desc - describes an IOCTL entry of the driver. + * @cmd: the IOCTL code as created by the kernel macros. + * @func: pointer to the driver's function that should be called for this IOCTL. + */ +struct hl_ioctl_desc { + unsigned int cmd; + hl_ioctl_t *func; +}; + + +/* + * Kernel module functions that can be accessed by entire module + */ + +/** + * hl_mem_area_inside_range() - Checks whether address+size are inside a range. + * @address: The start address of the area we want to validate. + * @size: The size in bytes of the area we want to validate. + * @range_start_address: The start address of the valid range. + * @range_end_address: The end address of the valid range. + * + * Return: true if the area is inside the valid range, false otherwise. + */ +static inline bool hl_mem_area_inside_range(u64 address, u32 size, + u64 range_start_address, u64 range_end_address) +{ + u64 end_address = address + size; + + if ((address >= range_start_address) && + (end_address <= range_end_address) && + (end_address > address)) + return true; + + return false; +} + +/** + * hl_mem_area_crosses_range() - Checks whether address+size crossing a range. + * @address: The start address of the area we want to validate. + * @size: The size in bytes of the area we want to validate. + * @range_start_address: The start address of the valid range. + * @range_end_address: The end address of the valid range. + * + * Return: true if the area overlaps part or all of the valid range, + * false otherwise. + */ +static inline bool hl_mem_area_crosses_range(u64 address, u32 size, + u64 range_start_address, u64 range_end_address) +{ + u64 end_address = address + size; + + if ((address >= range_start_address) && + (address < range_end_address)) + return true; + + if ((end_address >= range_start_address) && + (end_address < range_end_address)) + return true; + + if ((address < range_start_address) && + (end_address >= range_end_address)) + return true; + + return false; +} + +int hl_device_open(struct inode *inode, struct file *filp); +int hl_device_open_ctrl(struct inode *inode, struct file *filp); +bool hl_device_disabled_or_in_reset(struct hl_device *hdev); +enum hl_device_status hl_device_status(struct hl_device *hdev); +int hl_device_set_debug_mode(struct hl_device *hdev, bool enable); +int create_hdev(struct hl_device **dev, struct pci_dev *pdev, + enum hl_asic_type asic_type, int minor); +void destroy_hdev(struct hl_device *hdev); +int hl_hw_queues_create(struct hl_device *hdev); +void hl_hw_queues_destroy(struct hl_device *hdev); +int hl_hw_queue_send_cb_no_cmpl(struct hl_device *hdev, u32 hw_queue_id, + u32 cb_size, u64 cb_ptr); +int hl_hw_queue_schedule_cs(struct hl_cs *cs); +u32 hl_hw_queue_add_ptr(u32 ptr, u16 val); +void hl_hw_queue_inc_ci_kernel(struct hl_device *hdev, u32 hw_queue_id); +void hl_int_hw_queue_update_ci(struct hl_cs *cs); +void hl_hw_queue_reset(struct hl_device *hdev, bool hard_reset); + +#define hl_queue_inc_ptr(p) hl_hw_queue_add_ptr(p, 1) +#define hl_pi_2_offset(pi) ((pi) & (HL_QUEUE_LENGTH - 1)) + +int hl_cq_init(struct hl_device *hdev, struct hl_cq *q, u32 hw_queue_id); +void hl_cq_fini(struct hl_device *hdev, struct hl_cq *q); +int hl_eq_init(struct hl_device *hdev, struct hl_eq *q); +void hl_eq_fini(struct hl_device *hdev, struct hl_eq *q); +void hl_cq_reset(struct hl_device *hdev, struct hl_cq *q); +void hl_eq_reset(struct hl_device *hdev, struct hl_eq *q); +irqreturn_t hl_irq_handler_cq(int irq, void *arg); +irqreturn_t hl_irq_handler_eq(int irq, void *arg); +u32 hl_cq_inc_ptr(u32 ptr); + +int hl_asid_init(struct hl_device *hdev); +void hl_asid_fini(struct hl_device *hdev); +unsigned long hl_asid_alloc(struct hl_device *hdev); +void hl_asid_free(struct hl_device *hdev, unsigned long asid); + +int hl_ctx_create(struct hl_device *hdev, struct hl_fpriv *hpriv); +void hl_ctx_free(struct hl_device *hdev, struct hl_ctx *ctx); +int hl_ctx_init(struct hl_device *hdev, struct hl_ctx *ctx, bool is_kernel_ctx); +void hl_ctx_do_release(struct kref *ref); +void hl_ctx_get(struct hl_device *hdev, struct hl_ctx *ctx); +int hl_ctx_put(struct hl_ctx *ctx); +struct dma_fence *hl_ctx_get_fence(struct hl_ctx *ctx, u64 seq); +void hl_ctx_mgr_init(struct hl_ctx_mgr *mgr); +void hl_ctx_mgr_fini(struct hl_device *hdev, struct hl_ctx_mgr *mgr); + +int hl_device_init(struct hl_device *hdev, struct class *hclass); +void hl_device_fini(struct hl_device *hdev); +int hl_device_suspend(struct hl_device *hdev); +int hl_device_resume(struct hl_device *hdev); +int hl_device_reset(struct hl_device *hdev, bool hard_reset, + bool from_hard_reset_thread); +void hl_hpriv_get(struct hl_fpriv *hpriv); +void hl_hpriv_put(struct hl_fpriv *hpriv); +int hl_device_set_frequency(struct hl_device *hdev, enum hl_pll_frequency freq); +uint32_t hl_device_utilization(struct hl_device *hdev, uint32_t period_ms); + +int hl_build_hwmon_channel_info(struct hl_device *hdev, + struct armcp_sensor *sensors_arr); + +int hl_sysfs_init(struct hl_device *hdev); +void hl_sysfs_fini(struct hl_device *hdev); + +int hl_hwmon_init(struct hl_device *hdev); +void hl_hwmon_fini(struct hl_device *hdev); + +int hl_cb_create(struct hl_device *hdev, struct hl_cb_mgr *mgr, u32 cb_size, + u64 *handle, int ctx_id); +int hl_cb_destroy(struct hl_device *hdev, struct hl_cb_mgr *mgr, u64 cb_handle); +int hl_cb_mmap(struct hl_fpriv *hpriv, struct vm_area_struct *vma); +struct hl_cb *hl_cb_get(struct hl_device *hdev, struct hl_cb_mgr *mgr, + u32 handle); +void hl_cb_put(struct hl_cb *cb); +void hl_cb_mgr_init(struct hl_cb_mgr *mgr); +void hl_cb_mgr_fini(struct hl_device *hdev, struct hl_cb_mgr *mgr); +struct hl_cb *hl_cb_kernel_create(struct hl_device *hdev, u32 cb_size); +int hl_cb_pool_init(struct hl_device *hdev); +int hl_cb_pool_fini(struct hl_device *hdev); + +void hl_cs_rollback_all(struct hl_device *hdev); +struct hl_cs_job *hl_cs_allocate_job(struct hl_device *hdev, + enum hl_queue_type queue_type, bool is_kernel_allocated_cb); +void hl_sob_reset_error(struct kref *ref); + +void goya_set_asic_funcs(struct hl_device *hdev); +void gaudi_set_asic_funcs(struct hl_device *hdev); + +int hl_vm_ctx_init(struct hl_ctx *ctx); +void hl_vm_ctx_fini(struct hl_ctx *ctx); + +int hl_vm_init(struct hl_device *hdev); +void hl_vm_fini(struct hl_device *hdev); + +int hl_pin_host_memory(struct hl_device *hdev, u64 addr, u64 size, + struct hl_userptr *userptr); +void hl_unpin_host_memory(struct hl_device *hdev, struct hl_userptr *userptr); +void hl_userptr_delete_list(struct hl_device *hdev, + struct list_head *userptr_list); +bool hl_userptr_is_pinned(struct hl_device *hdev, u64 addr, u32 size, + struct list_head *userptr_list, + struct hl_userptr **userptr); + +int hl_mmu_init(struct hl_device *hdev); +void hl_mmu_fini(struct hl_device *hdev); +int hl_mmu_ctx_init(struct hl_ctx *ctx); +void hl_mmu_ctx_fini(struct hl_ctx *ctx); +int hl_mmu_map(struct hl_ctx *ctx, u64 virt_addr, u64 phys_addr, + u32 page_size, bool flush_pte); +int hl_mmu_unmap(struct hl_ctx *ctx, u64 virt_addr, u32 page_size, + bool flush_pte); +void hl_mmu_swap_out(struct hl_ctx *ctx); +void hl_mmu_swap_in(struct hl_ctx *ctx); + +int hl_fw_load_fw_to_device(struct hl_device *hdev, const char *fw_name, + void __iomem *dst); +int hl_fw_send_pci_access_msg(struct hl_device *hdev, u32 opcode); +int hl_fw_send_cpu_message(struct hl_device *hdev, u32 hw_queue_id, u32 *msg, + u16 len, u32 timeout, long *result); +int hl_fw_unmask_irq(struct hl_device *hdev, u16 event_type); +int hl_fw_unmask_irq_arr(struct hl_device *hdev, const u32 *irq_arr, + size_t irq_arr_size); +int hl_fw_test_cpu_queue(struct hl_device *hdev); +void *hl_fw_cpu_accessible_dma_pool_alloc(struct hl_device *hdev, size_t size, + dma_addr_t *dma_handle); +void hl_fw_cpu_accessible_dma_pool_free(struct hl_device *hdev, size_t size, + void *vaddr); +int hl_fw_send_heartbeat(struct hl_device *hdev); +int hl_fw_armcp_info_get(struct hl_device *hdev); +int hl_fw_get_eeprom_data(struct hl_device *hdev, void *data, size_t max_size); +int hl_fw_init_cpu(struct hl_device *hdev, u32 cpu_boot_status_reg, + u32 msg_to_cpu_reg, u32 cpu_msg_status_reg, + u32 boot_err0_reg, bool skip_bmc, + u32 cpu_timeout, u32 boot_fit_timeout); + +int hl_pci_bars_map(struct hl_device *hdev, const char * const name[3], + bool is_wc[3]); +int hl_pci_iatu_write(struct hl_device *hdev, u32 addr, u32 data); +int hl_pci_set_dram_bar_base(struct hl_device *hdev, u8 inbound_region, u8 bar, + u64 addr); +int hl_pci_set_inbound_region(struct hl_device *hdev, u8 region, + struct hl_inbound_pci_region *pci_region); +int hl_pci_set_outbound_region(struct hl_device *hdev, + struct hl_outbound_pci_region *pci_region); +int hl_pci_init(struct hl_device *hdev); +void hl_pci_fini(struct hl_device *hdev); + +long hl_get_frequency(struct hl_device *hdev, u32 pll_index, bool curr); +void hl_set_frequency(struct hl_device *hdev, u32 pll_index, u64 freq); +int hl_get_temperature(struct hl_device *hdev, + int sensor_index, u32 attr, long *value); +int hl_set_temperature(struct hl_device *hdev, + int sensor_index, u32 attr, long value); +int hl_get_voltage(struct hl_device *hdev, + int sensor_index, u32 attr, long *value); +int hl_get_current(struct hl_device *hdev, + int sensor_index, u32 attr, long *value); +int hl_get_fan_speed(struct hl_device *hdev, + int sensor_index, u32 attr, long *value); +int hl_get_pwm_info(struct hl_device *hdev, + int sensor_index, u32 attr, long *value); +void hl_set_pwm_info(struct hl_device *hdev, int sensor_index, u32 attr, + long value); +u64 hl_get_max_power(struct hl_device *hdev); +void hl_set_max_power(struct hl_device *hdev, u64 value); +int hl_set_voltage(struct hl_device *hdev, + int sensor_index, u32 attr, long value); +int hl_set_current(struct hl_device *hdev, + int sensor_index, u32 attr, long value); + +#ifdef CONFIG_DEBUG_FS + +void hl_debugfs_init(void); +void hl_debugfs_fini(void); +void hl_debugfs_add_device(struct hl_device *hdev); +void hl_debugfs_remove_device(struct hl_device *hdev); +void hl_debugfs_add_file(struct hl_fpriv *hpriv); +void hl_debugfs_remove_file(struct hl_fpriv *hpriv); +void hl_debugfs_add_cb(struct hl_cb *cb); +void hl_debugfs_remove_cb(struct hl_cb *cb); +void hl_debugfs_add_cs(struct hl_cs *cs); +void hl_debugfs_remove_cs(struct hl_cs *cs); +void hl_debugfs_add_job(struct hl_device *hdev, struct hl_cs_job *job); +void hl_debugfs_remove_job(struct hl_device *hdev, struct hl_cs_job *job); +void hl_debugfs_add_userptr(struct hl_device *hdev, struct hl_userptr *userptr); +void hl_debugfs_remove_userptr(struct hl_device *hdev, + struct hl_userptr *userptr); +void hl_debugfs_add_ctx_mem_hash(struct hl_device *hdev, struct hl_ctx *ctx); +void hl_debugfs_remove_ctx_mem_hash(struct hl_device *hdev, struct hl_ctx *ctx); + +#else + +static inline void __init hl_debugfs_init(void) +{ +} + +static inline void hl_debugfs_fini(void) +{ +} + +static inline void hl_debugfs_add_device(struct hl_device *hdev) +{ +} + +static inline void hl_debugfs_remove_device(struct hl_device *hdev) +{ +} + +static inline void hl_debugfs_add_file(struct hl_fpriv *hpriv) +{ +} + +static inline void hl_debugfs_remove_file(struct hl_fpriv *hpriv) +{ +} + +static inline void hl_debugfs_add_cb(struct hl_cb *cb) +{ +} + +static inline void hl_debugfs_remove_cb(struct hl_cb *cb) +{ +} + +static inline void hl_debugfs_add_cs(struct hl_cs *cs) +{ +} + +static inline void hl_debugfs_remove_cs(struct hl_cs *cs) +{ +} + +static inline void hl_debugfs_add_job(struct hl_device *hdev, + struct hl_cs_job *job) +{ +} + +static inline void hl_debugfs_remove_job(struct hl_device *hdev, + struct hl_cs_job *job) +{ +} + +static inline void hl_debugfs_add_userptr(struct hl_device *hdev, + struct hl_userptr *userptr) +{ +} + +static inline void hl_debugfs_remove_userptr(struct hl_device *hdev, + struct hl_userptr *userptr) +{ +} + +static inline void hl_debugfs_add_ctx_mem_hash(struct hl_device *hdev, + struct hl_ctx *ctx) +{ +} + +static inline void hl_debugfs_remove_ctx_mem_hash(struct hl_device *hdev, + struct hl_ctx *ctx) +{ +} + +#endif + +/* IOCTLs */ +long hl_ioctl(struct file *filep, unsigned int cmd, unsigned long arg); +long hl_ioctl_control(struct file *filep, unsigned int cmd, unsigned long arg); +int hl_cb_ioctl(struct hl_fpriv *hpriv, void *data); +int hl_cs_ioctl(struct hl_fpriv *hpriv, void *data); +int hl_cs_wait_ioctl(struct hl_fpriv *hpriv, void *data); +int hl_mem_ioctl(struct hl_fpriv *hpriv, void *data); + +#endif /* HABANALABSP_H_ */ diff --git a/drivers/misc/habanalabs/common/habanalabs_drv.c b/drivers/misc/habanalabs/common/habanalabs_drv.c new file mode 100644 index 000000000000..f38664b03865 --- /dev/null +++ b/drivers/misc/habanalabs/common/habanalabs_drv.c @@ -0,0 +1,529 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright 2016-2019 HabanaLabs, Ltd. + * All Rights Reserved. + * + */ + +#define pr_fmt(fmt) "habanalabs: " fmt + +#include "habanalabs.h" + +#include +#include + +#define HL_DRIVER_AUTHOR "HabanaLabs Kernel Driver Team" + +#define HL_DRIVER_DESC "Driver for HabanaLabs's AI Accelerators" + +MODULE_AUTHOR(HL_DRIVER_AUTHOR); +MODULE_DESCRIPTION(HL_DRIVER_DESC); +MODULE_LICENSE("GPL v2"); + +static int hl_major; +static struct class *hl_class; +static DEFINE_IDR(hl_devs_idr); +static DEFINE_MUTEX(hl_devs_idr_lock); + +static int timeout_locked = 5; +static int reset_on_lockup = 1; + +module_param(timeout_locked, int, 0444); +MODULE_PARM_DESC(timeout_locked, + "Device lockup timeout in seconds (0 = disabled, default 5s)"); + +module_param(reset_on_lockup, int, 0444); +MODULE_PARM_DESC(reset_on_lockup, + "Do device reset on lockup (0 = no, 1 = yes, default yes)"); + +#define PCI_VENDOR_ID_HABANALABS 0x1da3 + +#define PCI_IDS_GOYA 0x0001 +#define PCI_IDS_GAUDI 0x1000 + +static const struct pci_device_id ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_HABANALABS, PCI_IDS_GOYA), }, + { PCI_DEVICE(PCI_VENDOR_ID_HABANALABS, PCI_IDS_GAUDI), }, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, ids); + +/* + * get_asic_type - translate device id to asic type + * + * @device: id of the PCI device + * + * Translate device id to asic type. + * In case of unidentified device, return -1 + */ +static enum hl_asic_type get_asic_type(u16 device) +{ + enum hl_asic_type asic_type; + + switch (device) { + case PCI_IDS_GOYA: + asic_type = ASIC_GOYA; + break; + case PCI_IDS_GAUDI: + asic_type = ASIC_GAUDI; + break; + default: + asic_type = ASIC_INVALID; + break; + } + + return asic_type; +} + +/* + * hl_device_open - open function for habanalabs device + * + * @inode: pointer to inode structure + * @filp: pointer to file structure + * + * Called when process opens an habanalabs device. + */ +int hl_device_open(struct inode *inode, struct file *filp) +{ + struct hl_device *hdev; + struct hl_fpriv *hpriv; + int rc; + + mutex_lock(&hl_devs_idr_lock); + hdev = idr_find(&hl_devs_idr, iminor(inode)); + mutex_unlock(&hl_devs_idr_lock); + + if (!hdev) { + pr_err("Couldn't find device %d:%d\n", + imajor(inode), iminor(inode)); + return -ENXIO; + } + + hpriv = kzalloc(sizeof(*hpriv), GFP_KERNEL); + if (!hpriv) + return -ENOMEM; + + hpriv->hdev = hdev; + filp->private_data = hpriv; + hpriv->filp = filp; + mutex_init(&hpriv->restore_phase_mutex); + kref_init(&hpriv->refcount); + nonseekable_open(inode, filp); + + hl_cb_mgr_init(&hpriv->cb_mgr); + hl_ctx_mgr_init(&hpriv->ctx_mgr); + + hpriv->taskpid = find_get_pid(current->pid); + + mutex_lock(&hdev->fpriv_list_lock); + + if (hl_device_disabled_or_in_reset(hdev)) { + dev_err_ratelimited(hdev->dev, + "Can't open %s because it is disabled or in reset\n", + dev_name(hdev->dev)); + rc = -EPERM; + goto out_err; + } + + if (hdev->in_debug) { + dev_err_ratelimited(hdev->dev, + "Can't open %s because it is being debugged by another user\n", + dev_name(hdev->dev)); + rc = -EPERM; + goto out_err; + } + + if (hdev->compute_ctx) { + dev_dbg_ratelimited(hdev->dev, + "Can't open %s because another user is working on it\n", + dev_name(hdev->dev)); + rc = -EBUSY; + goto out_err; + } + + rc = hl_ctx_create(hdev, hpriv); + if (rc) { + dev_err(hdev->dev, "Failed to create context %d\n", rc); + goto out_err; + } + + /* Device is IDLE at this point so it is legal to change PLLs. + * There is no need to check anything because if the PLL is + * already HIGH, the set function will return without doing + * anything + */ + hl_device_set_frequency(hdev, PLL_HIGH); + + list_add(&hpriv->dev_node, &hdev->fpriv_list); + mutex_unlock(&hdev->fpriv_list_lock); + + hl_debugfs_add_file(hpriv); + + return 0; + +out_err: + mutex_unlock(&hdev->fpriv_list_lock); + + hl_cb_mgr_fini(hpriv->hdev, &hpriv->cb_mgr); + hl_ctx_mgr_fini(hpriv->hdev, &hpriv->ctx_mgr); + filp->private_data = NULL; + mutex_destroy(&hpriv->restore_phase_mutex); + put_pid(hpriv->taskpid); + + kfree(hpriv); + + return rc; +} + +int hl_device_open_ctrl(struct inode *inode, struct file *filp) +{ + struct hl_device *hdev; + struct hl_fpriv *hpriv; + int rc; + + mutex_lock(&hl_devs_idr_lock); + hdev = idr_find(&hl_devs_idr, iminor(inode)); + mutex_unlock(&hl_devs_idr_lock); + + if (!hdev) { + pr_err("Couldn't find device %d:%d\n", + imajor(inode), iminor(inode)); + return -ENXIO; + } + + hpriv = kzalloc(sizeof(*hpriv), GFP_KERNEL); + if (!hpriv) + return -ENOMEM; + + mutex_lock(&hdev->fpriv_list_lock); + + if (hl_device_disabled_or_in_reset(hdev)) { + dev_err_ratelimited(hdev->dev_ctrl, + "Can't open %s because it is disabled or in reset\n", + dev_name(hdev->dev_ctrl)); + rc = -EPERM; + goto out_err; + } + + list_add(&hpriv->dev_node, &hdev->fpriv_list); + mutex_unlock(&hdev->fpriv_list_lock); + + hpriv->hdev = hdev; + filp->private_data = hpriv; + hpriv->filp = filp; + hpriv->is_control = true; + nonseekable_open(inode, filp); + + hpriv->taskpid = find_get_pid(current->pid); + + return 0; + +out_err: + mutex_unlock(&hdev->fpriv_list_lock); + kfree(hpriv); + return rc; +} + +static void set_driver_behavior_per_device(struct hl_device *hdev) +{ + hdev->mmu_enable = 1; + hdev->cpu_enable = 1; + hdev->fw_loading = 1; + hdev->cpu_queues_enable = 1; + hdev->heartbeat = 1; + hdev->clock_gating = 1; + + hdev->reset_pcilink = 0; + hdev->axi_drain = 0; + hdev->sram_scrambler_enable = 1; + hdev->dram_scrambler_enable = 1; + hdev->bmc_enable = 1; + hdev->hard_reset_on_fw_events = 1; +} + +/* + * create_hdev - create habanalabs device instance + * + * @dev: will hold the pointer to the new habanalabs device structure + * @pdev: pointer to the pci device + * @asic_type: in case of simulator device, which device is it + * @minor: in case of simulator device, the minor of the device + * + * Allocate memory for habanalabs device and initialize basic fields + * Identify the ASIC type + * Allocate ID (minor) for the device (only for real devices) + */ +int create_hdev(struct hl_device **dev, struct pci_dev *pdev, + enum hl_asic_type asic_type, int minor) +{ + struct hl_device *hdev; + int rc, main_id, ctrl_id = 0; + + *dev = NULL; + + hdev = kzalloc(sizeof(*hdev), GFP_KERNEL); + if (!hdev) + return -ENOMEM; + + /* First, we must find out which ASIC are we handling. This is needed + * to configure the behavior of the driver (kernel parameters) + */ + if (pdev) { + hdev->asic_type = get_asic_type(pdev->device); + if (hdev->asic_type == ASIC_INVALID) { + dev_err(&pdev->dev, "Unsupported ASIC\n"); + rc = -ENODEV; + goto free_hdev; + } + } else { + hdev->asic_type = asic_type; + } + + hdev->major = hl_major; + hdev->reset_on_lockup = reset_on_lockup; + hdev->pldm = 0; + + set_driver_behavior_per_device(hdev); + + if (timeout_locked) + hdev->timeout_jiffies = msecs_to_jiffies(timeout_locked * 1000); + else + hdev->timeout_jiffies = MAX_SCHEDULE_TIMEOUT; + + hdev->disabled = true; + hdev->pdev = pdev; /* can be NULL in case of simulator device */ + + /* Set default DMA mask to 32 bits */ + hdev->dma_mask = 32; + + mutex_lock(&hl_devs_idr_lock); + + /* Always save 2 numbers, 1 for main device and 1 for control. + * They must be consecutive + */ + main_id = idr_alloc(&hl_devs_idr, hdev, 0, HL_MAX_MINORS, + GFP_KERNEL); + + if (main_id >= 0) + ctrl_id = idr_alloc(&hl_devs_idr, hdev, main_id + 1, + main_id + 2, GFP_KERNEL); + + mutex_unlock(&hl_devs_idr_lock); + + if ((main_id < 0) || (ctrl_id < 0)) { + if ((main_id == -ENOSPC) || (ctrl_id == -ENOSPC)) + pr_err("too many devices in the system\n"); + + if (main_id >= 0) { + mutex_lock(&hl_devs_idr_lock); + idr_remove(&hl_devs_idr, main_id); + mutex_unlock(&hl_devs_idr_lock); + } + + rc = -EBUSY; + goto free_hdev; + } + + hdev->id = main_id; + hdev->id_control = ctrl_id; + + *dev = hdev; + + return 0; + +free_hdev: + kfree(hdev); + return rc; +} + +/* + * destroy_hdev - destroy habanalabs device instance + * + * @dev: pointer to the habanalabs device structure + * + */ +void destroy_hdev(struct hl_device *hdev) +{ + /* Remove device from the device list */ + mutex_lock(&hl_devs_idr_lock); + idr_remove(&hl_devs_idr, hdev->id); + idr_remove(&hl_devs_idr, hdev->id_control); + mutex_unlock(&hl_devs_idr_lock); + + kfree(hdev); +} + +static int hl_pmops_suspend(struct device *dev) +{ + struct hl_device *hdev = dev_get_drvdata(dev); + + pr_debug("Going to suspend PCI device\n"); + + if (!hdev) { + pr_err("device pointer is NULL in suspend\n"); + return 0; + } + + return hl_device_suspend(hdev); +} + +static int hl_pmops_resume(struct device *dev) +{ + struct hl_device *hdev = dev_get_drvdata(dev); + + pr_debug("Going to resume PCI device\n"); + + if (!hdev) { + pr_err("device pointer is NULL in resume\n"); + return 0; + } + + return hl_device_resume(hdev); +} + +/* + * hl_pci_probe - probe PCI habanalabs devices + * + * @pdev: pointer to pci device + * @id: pointer to pci device id structure + * + * Standard PCI probe function for habanalabs device. + * Create a new habanalabs device and initialize it according to the + * device's type + */ +static int hl_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct hl_device *hdev; + int rc; + + dev_info(&pdev->dev, HL_NAME + " device found [%04x:%04x] (rev %x)\n", + (int)pdev->vendor, (int)pdev->device, (int)pdev->revision); + + rc = create_hdev(&hdev, pdev, ASIC_INVALID, -1); + if (rc) + return rc; + + pci_set_drvdata(pdev, hdev); + + rc = hl_device_init(hdev, hl_class); + if (rc) { + dev_err(&pdev->dev, "Fatal error during habanalabs device init\n"); + rc = -ENODEV; + goto disable_device; + } + + return 0; + +disable_device: + pci_set_drvdata(pdev, NULL); + destroy_hdev(hdev); + + return rc; +} + +/* + * hl_pci_remove - remove PCI habanalabs devices + * + * @pdev: pointer to pci device + * + * Standard PCI remove function for habanalabs device + */ +static void hl_pci_remove(struct pci_dev *pdev) +{ + struct hl_device *hdev; + + hdev = pci_get_drvdata(pdev); + if (!hdev) + return; + + hl_device_fini(hdev); + pci_set_drvdata(pdev, NULL); + + destroy_hdev(hdev); +} + +static const struct dev_pm_ops hl_pm_ops = { + .suspend = hl_pmops_suspend, + .resume = hl_pmops_resume, +}; + +static struct pci_driver hl_pci_driver = { + .name = HL_NAME, + .id_table = ids, + .probe = hl_pci_probe, + .remove = hl_pci_remove, + .driver.pm = &hl_pm_ops, +}; + +/* + * hl_init - Initialize the habanalabs kernel driver + */ +static int __init hl_init(void) +{ + int rc; + dev_t dev; + + pr_info("loading driver\n"); + + rc = alloc_chrdev_region(&dev, 0, HL_MAX_MINORS, HL_NAME); + if (rc < 0) { + pr_err("unable to get major\n"); + return rc; + } + + hl_major = MAJOR(dev); + + hl_class = class_create(THIS_MODULE, HL_NAME); + if (IS_ERR(hl_class)) { + pr_err("failed to allocate class\n"); + rc = PTR_ERR(hl_class); + goto remove_major; + } + + hl_debugfs_init(); + + rc = pci_register_driver(&hl_pci_driver); + if (rc) { + pr_err("failed to register pci device\n"); + goto remove_debugfs; + } + + pr_debug("driver loaded\n"); + + return 0; + +remove_debugfs: + hl_debugfs_fini(); + class_destroy(hl_class); +remove_major: + unregister_chrdev_region(MKDEV(hl_major, 0), HL_MAX_MINORS); + return rc; +} + +/* + * hl_exit - Release all resources of the habanalabs kernel driver + */ +static void __exit hl_exit(void) +{ + pci_unregister_driver(&hl_pci_driver); + + /* + * Removing debugfs must be after all devices or simulator devices + * have been removed because otherwise we get a bug in the + * debugfs module for referencing NULL objects + */ + hl_debugfs_fini(); + + class_destroy(hl_class); + unregister_chrdev_region(MKDEV(hl_major, 0), HL_MAX_MINORS); + + idr_destroy(&hl_devs_idr); + + pr_debug("driver removed\n"); +} + +module_init(hl_init); +module_exit(hl_exit); diff --git a/drivers/misc/habanalabs/common/habanalabs_ioctl.c b/drivers/misc/habanalabs/common/habanalabs_ioctl.c new file mode 100644 index 000000000000..5af1c03da473 --- /dev/null +++ b/drivers/misc/habanalabs/common/habanalabs_ioctl.c @@ -0,0 +1,546 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright 2016-2019 HabanaLabs, Ltd. + * All Rights Reserved. + */ + +#include +#include "habanalabs.h" + +#include +#include +#include + +static u32 hl_debug_struct_size[HL_DEBUG_OP_TIMESTAMP + 1] = { + [HL_DEBUG_OP_ETR] = sizeof(struct hl_debug_params_etr), + [HL_DEBUG_OP_ETF] = sizeof(struct hl_debug_params_etf), + [HL_DEBUG_OP_STM] = sizeof(struct hl_debug_params_stm), + [HL_DEBUG_OP_FUNNEL] = 0, + [HL_DEBUG_OP_BMON] = sizeof(struct hl_debug_params_bmon), + [HL_DEBUG_OP_SPMU] = sizeof(struct hl_debug_params_spmu), + [HL_DEBUG_OP_TIMESTAMP] = 0 + +}; + +static int device_status_info(struct hl_device *hdev, struct hl_info_args *args) +{ + struct hl_info_device_status dev_stat = {0}; + u32 size = args->return_size; + void __user *out = (void __user *) (uintptr_t) args->return_pointer; + + if ((!size) || (!out)) + return -EINVAL; + + dev_stat.status = hl_device_status(hdev); + + return copy_to_user(out, &dev_stat, + min((size_t)size, sizeof(dev_stat))) ? -EFAULT : 0; +} + +static int hw_ip_info(struct hl_device *hdev, struct hl_info_args *args) +{ + struct hl_info_hw_ip_info hw_ip = {0}; + u32 size = args->return_size; + void __user *out = (void __user *) (uintptr_t) args->return_pointer; + struct asic_fixed_properties *prop = &hdev->asic_prop; + u64 sram_kmd_size, dram_kmd_size; + + if ((!size) || (!out)) + return -EINVAL; + + sram_kmd_size = (prop->sram_user_base_address - + prop->sram_base_address); + dram_kmd_size = (prop->dram_user_base_address - + prop->dram_base_address); + + hw_ip.device_id = hdev->asic_funcs->get_pci_id(hdev); + hw_ip.sram_base_address = prop->sram_user_base_address; + hw_ip.dram_base_address = prop->dram_user_base_address; + hw_ip.tpc_enabled_mask = prop->tpc_enabled_mask; + hw_ip.sram_size = prop->sram_size - sram_kmd_size; + hw_ip.dram_size = prop->dram_size - dram_kmd_size; + if (hw_ip.dram_size > PAGE_SIZE) + hw_ip.dram_enabled = 1; + hw_ip.num_of_events = prop->num_of_events; + + memcpy(hw_ip.armcp_version, prop->armcp_info.armcp_version, + min(VERSION_MAX_LEN, HL_INFO_VERSION_MAX_LEN)); + + memcpy(hw_ip.card_name, prop->armcp_info.card_name, + min(CARD_NAME_MAX_LEN, HL_INFO_CARD_NAME_MAX_LEN)); + + hw_ip.armcp_cpld_version = le32_to_cpu(prop->armcp_info.cpld_version); + hw_ip.module_id = le32_to_cpu(prop->armcp_info.card_location); + + hw_ip.psoc_pci_pll_nr = prop->psoc_pci_pll_nr; + hw_ip.psoc_pci_pll_nf = prop->psoc_pci_pll_nf; + hw_ip.psoc_pci_pll_od = prop->psoc_pci_pll_od; + hw_ip.psoc_pci_pll_div_factor = prop->psoc_pci_pll_div_factor; + + return copy_to_user(out, &hw_ip, + min((size_t)size, sizeof(hw_ip))) ? -EFAULT : 0; +} + +static int hw_events_info(struct hl_device *hdev, bool aggregate, + struct hl_info_args *args) +{ + u32 size, max_size = args->return_size; + void __user *out = (void __user *) (uintptr_t) args->return_pointer; + void *arr; + + if ((!max_size) || (!out)) + return -EINVAL; + + arr = hdev->asic_funcs->get_events_stat(hdev, aggregate, &size); + + return copy_to_user(out, arr, min(max_size, size)) ? -EFAULT : 0; +} + +static int dram_usage_info(struct hl_fpriv *hpriv, struct hl_info_args *args) +{ + struct hl_device *hdev = hpriv->hdev; + struct hl_info_dram_usage dram_usage = {0}; + u32 max_size = args->return_size; + void __user *out = (void __user *) (uintptr_t) args->return_pointer; + struct asic_fixed_properties *prop = &hdev->asic_prop; + u64 dram_kmd_size; + + if ((!max_size) || (!out)) + return -EINVAL; + + dram_kmd_size = (prop->dram_user_base_address - + prop->dram_base_address); + dram_usage.dram_free_mem = (prop->dram_size - dram_kmd_size) - + atomic64_read(&hdev->dram_used_mem); + if (hpriv->ctx) + dram_usage.ctx_dram_mem = + atomic64_read(&hpriv->ctx->dram_phys_mem); + + return copy_to_user(out, &dram_usage, + min((size_t) max_size, sizeof(dram_usage))) ? -EFAULT : 0; +} + +static int hw_idle(struct hl_device *hdev, struct hl_info_args *args) +{ + struct hl_info_hw_idle hw_idle = {0}; + u32 max_size = args->return_size; + void __user *out = (void __user *) (uintptr_t) args->return_pointer; + + if ((!max_size) || (!out)) + return -EINVAL; + + hw_idle.is_idle = hdev->asic_funcs->is_device_idle(hdev, + &hw_idle.busy_engines_mask, NULL); + + return copy_to_user(out, &hw_idle, + min((size_t) max_size, sizeof(hw_idle))) ? -EFAULT : 0; +} + +static int debug_coresight(struct hl_device *hdev, struct hl_debug_args *args) +{ + struct hl_debug_params *params; + void *input = NULL, *output = NULL; + int rc; + + params = kzalloc(sizeof(*params), GFP_KERNEL); + if (!params) + return -ENOMEM; + + params->reg_idx = args->reg_idx; + params->enable = args->enable; + params->op = args->op; + + if (args->input_ptr && args->input_size) { + input = kzalloc(hl_debug_struct_size[args->op], GFP_KERNEL); + if (!input) { + rc = -ENOMEM; + goto out; + } + + if (copy_from_user(input, u64_to_user_ptr(args->input_ptr), + args->input_size)) { + rc = -EFAULT; + dev_err(hdev->dev, "failed to copy input debug data\n"); + goto out; + } + + params->input = input; + } + + if (args->output_ptr && args->output_size) { + output = kzalloc(args->output_size, GFP_KERNEL); + if (!output) { + rc = -ENOMEM; + goto out; + } + + params->output = output; + params->output_size = args->output_size; + } + + rc = hdev->asic_funcs->debug_coresight(hdev, params); + if (rc) { + dev_err(hdev->dev, + "debug coresight operation failed %d\n", rc); + goto out; + } + + if (output && copy_to_user((void __user *) (uintptr_t) args->output_ptr, + output, args->output_size)) { + dev_err(hdev->dev, "copy to user failed in debug ioctl\n"); + rc = -EFAULT; + goto out; + } + + +out: + kfree(params); + kfree(output); + kfree(input); + + return rc; +} + +static int device_utilization(struct hl_device *hdev, struct hl_info_args *args) +{ + struct hl_info_device_utilization device_util = {0}; + u32 max_size = args->return_size; + void __user *out = (void __user *) (uintptr_t) args->return_pointer; + + if ((!max_size) || (!out)) + return -EINVAL; + + if ((args->period_ms < 100) || (args->period_ms > 1000) || + (args->period_ms % 100)) { + dev_err(hdev->dev, + "period %u must be between 100 - 1000 and must be divisible by 100\n", + args->period_ms); + return -EINVAL; + } + + device_util.utilization = hl_device_utilization(hdev, args->period_ms); + + return copy_to_user(out, &device_util, + min((size_t) max_size, sizeof(device_util))) ? -EFAULT : 0; +} + +static int get_clk_rate(struct hl_device *hdev, struct hl_info_args *args) +{ + struct hl_info_clk_rate clk_rate = {0}; + u32 max_size = args->return_size; + void __user *out = (void __user *) (uintptr_t) args->return_pointer; + int rc; + + if ((!max_size) || (!out)) + return -EINVAL; + + rc = hdev->asic_funcs->get_clk_rate(hdev, &clk_rate.cur_clk_rate_mhz, + &clk_rate.max_clk_rate_mhz); + if (rc) + return rc; + + return copy_to_user(out, &clk_rate, + min((size_t) max_size, sizeof(clk_rate))) ? -EFAULT : 0; +} + +static int get_reset_count(struct hl_device *hdev, struct hl_info_args *args) +{ + struct hl_info_reset_count reset_count = {0}; + u32 max_size = args->return_size; + void __user *out = (void __user *) (uintptr_t) args->return_pointer; + + if ((!max_size) || (!out)) + return -EINVAL; + + reset_count.hard_reset_cnt = hdev->hard_reset_cnt; + reset_count.soft_reset_cnt = hdev->soft_reset_cnt; + + return copy_to_user(out, &reset_count, + min((size_t) max_size, sizeof(reset_count))) ? -EFAULT : 0; +} + +static int time_sync_info(struct hl_device *hdev, struct hl_info_args *args) +{ + struct hl_info_time_sync time_sync = {0}; + u32 max_size = args->return_size; + void __user *out = (void __user *) (uintptr_t) args->return_pointer; + + if ((!max_size) || (!out)) + return -EINVAL; + + time_sync.device_time = hdev->asic_funcs->get_device_time(hdev); + time_sync.host_time = ktime_get_raw_ns(); + + return copy_to_user(out, &time_sync, + min((size_t) max_size, sizeof(time_sync))) ? -EFAULT : 0; +} + +static int cs_counters_info(struct hl_fpriv *hpriv, struct hl_info_args *args) +{ + struct hl_device *hdev = hpriv->hdev; + struct hl_info_cs_counters cs_counters = {0}; + u32 max_size = args->return_size; + void __user *out = (void __user *) (uintptr_t) args->return_pointer; + + if ((!max_size) || (!out)) + return -EINVAL; + + memcpy(&cs_counters.cs_counters, &hdev->aggregated_cs_counters, + sizeof(struct hl_cs_counters)); + + if (hpriv->ctx) + memcpy(&cs_counters.ctx_cs_counters, &hpriv->ctx->cs_counters, + sizeof(struct hl_cs_counters)); + + return copy_to_user(out, &cs_counters, + min((size_t) max_size, sizeof(cs_counters))) ? -EFAULT : 0; +} + +static int _hl_info_ioctl(struct hl_fpriv *hpriv, void *data, + struct device *dev) +{ + struct hl_info_args *args = data; + struct hl_device *hdev = hpriv->hdev; + int rc; + + /* + * Information is returned for the following opcodes even if the device + * is disabled or in reset. + */ + switch (args->op) { + case HL_INFO_HW_IP_INFO: + return hw_ip_info(hdev, args); + + case HL_INFO_DEVICE_STATUS: + return device_status_info(hdev, args); + + case HL_INFO_RESET_COUNT: + return get_reset_count(hdev, args); + + default: + break; + } + + if (hl_device_disabled_or_in_reset(hdev)) { + dev_warn_ratelimited(dev, + "Device is %s. Can't execute INFO IOCTL\n", + atomic_read(&hdev->in_reset) ? "in_reset" : "disabled"); + return -EBUSY; + } + + switch (args->op) { + case HL_INFO_HW_EVENTS: + rc = hw_events_info(hdev, false, args); + break; + + case HL_INFO_DRAM_USAGE: + rc = dram_usage_info(hpriv, args); + break; + + case HL_INFO_HW_IDLE: + rc = hw_idle(hdev, args); + break; + + case HL_INFO_DEVICE_UTILIZATION: + rc = device_utilization(hdev, args); + break; + + case HL_INFO_HW_EVENTS_AGGREGATE: + rc = hw_events_info(hdev, true, args); + break; + + case HL_INFO_CLK_RATE: + rc = get_clk_rate(hdev, args); + break; + + case HL_INFO_TIME_SYNC: + return time_sync_info(hdev, args); + + case HL_INFO_CS_COUNTERS: + return cs_counters_info(hpriv, args); + + default: + dev_err(dev, "Invalid request %d\n", args->op); + rc = -ENOTTY; + break; + } + + return rc; +} + +static int hl_info_ioctl(struct hl_fpriv *hpriv, void *data) +{ + return _hl_info_ioctl(hpriv, data, hpriv->hdev->dev); +} + +static int hl_info_ioctl_control(struct hl_fpriv *hpriv, void *data) +{ + return _hl_info_ioctl(hpriv, data, hpriv->hdev->dev_ctrl); +} + +static int hl_debug_ioctl(struct hl_fpriv *hpriv, void *data) +{ + struct hl_debug_args *args = data; + struct hl_device *hdev = hpriv->hdev; + int rc = 0; + + if (hl_device_disabled_or_in_reset(hdev)) { + dev_warn_ratelimited(hdev->dev, + "Device is %s. Can't execute DEBUG IOCTL\n", + atomic_read(&hdev->in_reset) ? "in_reset" : "disabled"); + return -EBUSY; + } + + switch (args->op) { + case HL_DEBUG_OP_ETR: + case HL_DEBUG_OP_ETF: + case HL_DEBUG_OP_STM: + case HL_DEBUG_OP_FUNNEL: + case HL_DEBUG_OP_BMON: + case HL_DEBUG_OP_SPMU: + case HL_DEBUG_OP_TIMESTAMP: + if (!hdev->in_debug) { + dev_err_ratelimited(hdev->dev, + "Rejecting debug configuration request because device not in debug mode\n"); + return -EFAULT; + } + args->input_size = + min(args->input_size, hl_debug_struct_size[args->op]); + rc = debug_coresight(hdev, args); + break; + case HL_DEBUG_OP_SET_MODE: + rc = hl_device_set_debug_mode(hdev, (bool) args->enable); + break; + default: + dev_err(hdev->dev, "Invalid request %d\n", args->op); + rc = -ENOTTY; + break; + } + + return rc; +} + +#define HL_IOCTL_DEF(ioctl, _func) \ + [_IOC_NR(ioctl)] = {.cmd = ioctl, .func = _func} + +static const struct hl_ioctl_desc hl_ioctls[] = { + HL_IOCTL_DEF(HL_IOCTL_INFO, hl_info_ioctl), + HL_IOCTL_DEF(HL_IOCTL_CB, hl_cb_ioctl), + HL_IOCTL_DEF(HL_IOCTL_CS, hl_cs_ioctl), + HL_IOCTL_DEF(HL_IOCTL_WAIT_CS, hl_cs_wait_ioctl), + HL_IOCTL_DEF(HL_IOCTL_MEMORY, hl_mem_ioctl), + HL_IOCTL_DEF(HL_IOCTL_DEBUG, hl_debug_ioctl) +}; + +static const struct hl_ioctl_desc hl_ioctls_control[] = { + HL_IOCTL_DEF(HL_IOCTL_INFO, hl_info_ioctl_control) +}; + +static long _hl_ioctl(struct file *filep, unsigned int cmd, unsigned long arg, + const struct hl_ioctl_desc *ioctl, struct device *dev) +{ + struct hl_fpriv *hpriv = filep->private_data; + struct hl_device *hdev = hpriv->hdev; + unsigned int nr = _IOC_NR(cmd); + char stack_kdata[128] = {0}; + char *kdata = NULL; + unsigned int usize, asize; + hl_ioctl_t *func; + u32 hl_size; + int retcode; + + if (hdev->hard_reset_pending) { + dev_crit_ratelimited(hdev->dev_ctrl, + "Device HARD reset pending! Please close FD\n"); + return -ENODEV; + } + + /* Do not trust userspace, use our own definition */ + func = ioctl->func; + + if (unlikely(!func)) { + dev_dbg(dev, "no function\n"); + retcode = -ENOTTY; + goto out_err; + } + + hl_size = _IOC_SIZE(ioctl->cmd); + usize = asize = _IOC_SIZE(cmd); + if (hl_size > asize) + asize = hl_size; + + cmd = ioctl->cmd; + + if (cmd & (IOC_IN | IOC_OUT)) { + if (asize <= sizeof(stack_kdata)) { + kdata = stack_kdata; + } else { + kdata = kzalloc(asize, GFP_KERNEL); + if (!kdata) { + retcode = -ENOMEM; + goto out_err; + } + } + } + + if (cmd & IOC_IN) { + if (copy_from_user(kdata, (void __user *)arg, usize)) { + retcode = -EFAULT; + goto out_err; + } + } else if (cmd & IOC_OUT) { + memset(kdata, 0, usize); + } + + retcode = func(hpriv, kdata); + + if ((cmd & IOC_OUT) && copy_to_user((void __user *)arg, kdata, usize)) + retcode = -EFAULT; + +out_err: + if (retcode) + dev_dbg(dev, "error in ioctl: pid=%d, cmd=0x%02x, nr=0x%02x\n", + task_pid_nr(current), cmd, nr); + + if (kdata != stack_kdata) + kfree(kdata); + + return retcode; +} + +long hl_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) +{ + struct hl_fpriv *hpriv = filep->private_data; + struct hl_device *hdev = hpriv->hdev; + const struct hl_ioctl_desc *ioctl = NULL; + unsigned int nr = _IOC_NR(cmd); + + if ((nr >= HL_COMMAND_START) && (nr < HL_COMMAND_END)) { + ioctl = &hl_ioctls[nr]; + } else { + dev_err(hdev->dev, "invalid ioctl: pid=%d, nr=0x%02x\n", + task_pid_nr(current), nr); + return -ENOTTY; + } + + return _hl_ioctl(filep, cmd, arg, ioctl, hdev->dev); +} + +long hl_ioctl_control(struct file *filep, unsigned int cmd, unsigned long arg) +{ + struct hl_fpriv *hpriv = filep->private_data; + struct hl_device *hdev = hpriv->hdev; + const struct hl_ioctl_desc *ioctl = NULL; + unsigned int nr = _IOC_NR(cmd); + + if (nr == _IOC_NR(HL_IOCTL_INFO)) { + ioctl = &hl_ioctls_control[nr]; + } else { + dev_err(hdev->dev_ctrl, "invalid ioctl: pid=%d, nr=0x%02x\n", + task_pid_nr(current), nr); + return -ENOTTY; + } + + return _hl_ioctl(filep, cmd, arg, ioctl, hdev->dev_ctrl); +} diff --git a/drivers/misc/habanalabs/common/hw_queue.c b/drivers/misc/habanalabs/common/hw_queue.c new file mode 100644 index 000000000000..287681646071 --- /dev/null +++ b/drivers/misc/habanalabs/common/hw_queue.c @@ -0,0 +1,918 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright 2016-2019 HabanaLabs, Ltd. + * All Rights Reserved. + */ + +#include "habanalabs.h" + +#include + +/* + * hl_queue_add_ptr - add to pi or ci and checks if it wraps around + * + * @ptr: the current pi/ci value + * @val: the amount to add + * + * Add val to ptr. It can go until twice the queue length. + */ +inline u32 hl_hw_queue_add_ptr(u32 ptr, u16 val) +{ + ptr += val; + ptr &= ((HL_QUEUE_LENGTH << 1) - 1); + return ptr; +} +static inline int queue_ci_get(atomic_t *ci, u32 queue_len) +{ + return atomic_read(ci) & ((queue_len << 1) - 1); +} + +static inline int queue_free_slots(struct hl_hw_queue *q, u32 queue_len) +{ + int delta = (q->pi - queue_ci_get(&q->ci, queue_len)); + + if (delta >= 0) + return (queue_len - delta); + else + return (abs(delta) - queue_len); +} + +void hl_int_hw_queue_update_ci(struct hl_cs *cs) +{ + struct hl_device *hdev = cs->ctx->hdev; + struct hl_hw_queue *q; + int i; + + if (hdev->disabled) + return; + + q = &hdev->kernel_queues[0]; + for (i = 0 ; i < hdev->asic_prop.max_queues ; i++, q++) { + if (q->queue_type == QUEUE_TYPE_INT) + atomic_add(cs->jobs_in_queue_cnt[i], &q->ci); + } +} + +/* + * ext_and_hw_queue_submit_bd() - Submit a buffer descriptor to an external or a + * H/W queue. + * @hdev: pointer to habanalabs device structure + * @q: pointer to habanalabs queue structure + * @ctl: BD's control word + * @len: BD's length + * @ptr: BD's pointer + * + * This function assumes there is enough space on the queue to submit a new + * BD to it. It initializes the next BD and calls the device specific + * function to set the pi (and doorbell) + * + * This function must be called when the scheduler mutex is taken + * + */ +static void ext_and_hw_queue_submit_bd(struct hl_device *hdev, + struct hl_hw_queue *q, u32 ctl, u32 len, u64 ptr) +{ + struct hl_bd *bd; + + bd = (struct hl_bd *) (uintptr_t) q->kernel_address; + bd += hl_pi_2_offset(q->pi); + bd->ctl = cpu_to_le32(ctl); + bd->len = cpu_to_le32(len); + bd->ptr = cpu_to_le64(ptr); + + q->pi = hl_queue_inc_ptr(q->pi); + hdev->asic_funcs->ring_doorbell(hdev, q->hw_queue_id, q->pi); +} + +/* + * ext_queue_sanity_checks - perform some sanity checks on external queue + * + * @hdev : pointer to hl_device structure + * @q : pointer to hl_hw_queue structure + * @num_of_entries : how many entries to check for space + * @reserve_cq_entry : whether to reserve an entry in the cq + * + * H/W queues spinlock should be taken before calling this function + * + * Perform the following: + * - Make sure we have enough space in the h/w queue + * - Make sure we have enough space in the completion queue + * - Reserve space in the completion queue (needs to be reversed if there + * is a failure down the road before the actual submission of work). Only + * do this action if reserve_cq_entry is true + * + */ +static int ext_queue_sanity_checks(struct hl_device *hdev, + struct hl_hw_queue *q, int num_of_entries, + bool reserve_cq_entry) +{ + atomic_t *free_slots = + &hdev->completion_queue[q->cq_id].free_slots_cnt; + int free_slots_cnt; + + /* Check we have enough space in the queue */ + free_slots_cnt = queue_free_slots(q, HL_QUEUE_LENGTH); + + if (free_slots_cnt < num_of_entries) { + dev_dbg(hdev->dev, "Queue %d doesn't have room for %d CBs\n", + q->hw_queue_id, num_of_entries); + return -EAGAIN; + } + + if (reserve_cq_entry) { + /* + * Check we have enough space in the completion queue + * Add -1 to counter (decrement) unless counter was already 0 + * In that case, CQ is full so we can't submit a new CB because + * we won't get ack on its completion + * atomic_add_unless will return 0 if counter was already 0 + */ + if (atomic_add_negative(num_of_entries * -1, free_slots)) { + dev_dbg(hdev->dev, "No space for %d on CQ %d\n", + num_of_entries, q->hw_queue_id); + atomic_add(num_of_entries, free_slots); + return -EAGAIN; + } + } + + return 0; +} + +/* + * int_queue_sanity_checks - perform some sanity checks on internal queue + * + * @hdev : pointer to hl_device structure + * @q : pointer to hl_hw_queue structure + * @num_of_entries : how many entries to check for space + * + * H/W queues spinlock should be taken before calling this function + * + * Perform the following: + * - Make sure we have enough space in the h/w queue + * + */ +static int int_queue_sanity_checks(struct hl_device *hdev, + struct hl_hw_queue *q, + int num_of_entries) +{ + int free_slots_cnt; + + if (num_of_entries > q->int_queue_len) { + dev_err(hdev->dev, + "Cannot populate queue %u with %u jobs\n", + q->hw_queue_id, num_of_entries); + return -ENOMEM; + } + + /* Check we have enough space in the queue */ + free_slots_cnt = queue_free_slots(q, q->int_queue_len); + + if (free_slots_cnt < num_of_entries) { + dev_dbg(hdev->dev, "Queue %d doesn't have room for %d CBs\n", + q->hw_queue_id, num_of_entries); + return -EAGAIN; + } + + return 0; +} + +/* + * hw_queue_sanity_checks() - Make sure we have enough space in the h/w queue + * @hdev: Pointer to hl_device structure. + * @q: Pointer to hl_hw_queue structure. + * @num_of_entries: How many entries to check for space. + * + * Notice: We do not reserve queue entries so this function mustn't be called + * more than once per CS for the same queue + * + */ +static int hw_queue_sanity_checks(struct hl_device *hdev, struct hl_hw_queue *q, + int num_of_entries) +{ + int free_slots_cnt; + + /* Check we have enough space in the queue */ + free_slots_cnt = queue_free_slots(q, HL_QUEUE_LENGTH); + + if (free_slots_cnt < num_of_entries) { + dev_dbg(hdev->dev, "Queue %d doesn't have room for %d CBs\n", + q->hw_queue_id, num_of_entries); + return -EAGAIN; + } + + return 0; +} + +/* + * hl_hw_queue_send_cb_no_cmpl - send a single CB (not a JOB) without completion + * + * @hdev: pointer to hl_device structure + * @hw_queue_id: Queue's type + * @cb_size: size of CB + * @cb_ptr: pointer to CB location + * + * This function sends a single CB, that must NOT generate a completion entry + * + */ +int hl_hw_queue_send_cb_no_cmpl(struct hl_device *hdev, u32 hw_queue_id, + u32 cb_size, u64 cb_ptr) +{ + struct hl_hw_queue *q = &hdev->kernel_queues[hw_queue_id]; + int rc = 0; + + /* + * The CPU queue is a synchronous queue with an effective depth of + * a single entry (although it is allocated with room for multiple + * entries). Therefore, there is a different lock, called + * send_cpu_message_lock, that serializes accesses to the CPU queue. + * As a result, we don't need to lock the access to the entire H/W + * queues module when submitting a JOB to the CPU queue + */ + if (q->queue_type != QUEUE_TYPE_CPU) + hdev->asic_funcs->hw_queues_lock(hdev); + + if (hdev->disabled) { + rc = -EPERM; + goto out; + } + + /* + * hl_hw_queue_send_cb_no_cmpl() is called for queues of a H/W queue + * type only on init phase, when the queues are empty and being tested, + * so there is no need for sanity checks. + */ + if (q->queue_type != QUEUE_TYPE_HW) { + rc = ext_queue_sanity_checks(hdev, q, 1, false); + if (rc) + goto out; + } + + ext_and_hw_queue_submit_bd(hdev, q, 0, cb_size, cb_ptr); + +out: + if (q->queue_type != QUEUE_TYPE_CPU) + hdev->asic_funcs->hw_queues_unlock(hdev); + + return rc; +} + +/* + * ext_queue_schedule_job - submit a JOB to an external queue + * + * @job: pointer to the job that needs to be submitted to the queue + * + * This function must be called when the scheduler mutex is taken + * + */ +static void ext_queue_schedule_job(struct hl_cs_job *job) +{ + struct hl_device *hdev = job->cs->ctx->hdev; + struct hl_hw_queue *q = &hdev->kernel_queues[job->hw_queue_id]; + struct hl_cq_entry cq_pkt; + struct hl_cq *cq; + u64 cq_addr; + struct hl_cb *cb; + u32 ctl; + u32 len; + u64 ptr; + + /* + * Update the JOB ID inside the BD CTL so the device would know what + * to write in the completion queue + */ + ctl = ((q->pi << BD_CTL_SHADOW_INDEX_SHIFT) & BD_CTL_SHADOW_INDEX_MASK); + + cb = job->patched_cb; + len = job->job_cb_size; + ptr = cb->bus_address; + + cq_pkt.data = cpu_to_le32( + ((q->pi << CQ_ENTRY_SHADOW_INDEX_SHIFT) + & CQ_ENTRY_SHADOW_INDEX_MASK) | + (1 << CQ_ENTRY_SHADOW_INDEX_VALID_SHIFT) | + (1 << CQ_ENTRY_READY_SHIFT)); + + /* + * No need to protect pi_offset because scheduling to the + * H/W queues is done under the scheduler mutex + * + * No need to check if CQ is full because it was already + * checked in ext_queue_sanity_checks + */ + cq = &hdev->completion_queue[q->cq_id]; + cq_addr = cq->bus_address + cq->pi * sizeof(struct hl_cq_entry); + + hdev->asic_funcs->add_end_of_cb_packets(hdev, cb->kernel_address, len, + cq_addr, + le32_to_cpu(cq_pkt.data), + q->msi_vec, + job->contains_dma_pkt); + + q->shadow_queue[hl_pi_2_offset(q->pi)] = job; + + cq->pi = hl_cq_inc_ptr(cq->pi); + + ext_and_hw_queue_submit_bd(hdev, q, ctl, len, ptr); +} + +/* + * int_queue_schedule_job - submit a JOB to an internal queue + * + * @job: pointer to the job that needs to be submitted to the queue + * + * This function must be called when the scheduler mutex is taken + * + */ +static void int_queue_schedule_job(struct hl_cs_job *job) +{ + struct hl_device *hdev = job->cs->ctx->hdev; + struct hl_hw_queue *q = &hdev->kernel_queues[job->hw_queue_id]; + struct hl_bd bd; + __le64 *pi; + + bd.ctl = 0; + bd.len = cpu_to_le32(job->job_cb_size); + bd.ptr = cpu_to_le64((u64) (uintptr_t) job->user_cb); + + pi = (__le64 *) (uintptr_t) (q->kernel_address + + ((q->pi & (q->int_queue_len - 1)) * sizeof(bd))); + + q->pi++; + q->pi &= ((q->int_queue_len << 1) - 1); + + hdev->asic_funcs->pqe_write(hdev, pi, &bd); + + hdev->asic_funcs->ring_doorbell(hdev, q->hw_queue_id, q->pi); +} + +/* + * hw_queue_schedule_job - submit a JOB to a H/W queue + * + * @job: pointer to the job that needs to be submitted to the queue + * + * This function must be called when the scheduler mutex is taken + * + */ +static void hw_queue_schedule_job(struct hl_cs_job *job) +{ + struct hl_device *hdev = job->cs->ctx->hdev; + struct hl_hw_queue *q = &hdev->kernel_queues[job->hw_queue_id]; + u64 ptr; + u32 offset, ctl, len; + + /* + * Upon PQE completion, COMP_DATA is used as the write data to the + * completion queue (QMAN HBW message), and COMP_OFFSET is used as the + * write address offset in the SM block (QMAN LBW message). + * The write address offset is calculated as "COMP_OFFSET << 2". + */ + offset = job->cs->sequence & (hdev->asic_prop.max_pending_cs - 1); + ctl = ((offset << BD_CTL_COMP_OFFSET_SHIFT) & BD_CTL_COMP_OFFSET_MASK) | + ((q->pi << BD_CTL_COMP_DATA_SHIFT) & BD_CTL_COMP_DATA_MASK); + + len = job->job_cb_size; + + /* + * A patched CB is created only if a user CB was allocated by driver and + * MMU is disabled. If MMU is enabled, the user CB should be used + * instead. If the user CB wasn't allocated by driver, assume that it + * holds an address. + */ + if (job->patched_cb) + ptr = job->patched_cb->bus_address; + else if (job->is_kernel_allocated_cb) + ptr = job->user_cb->bus_address; + else + ptr = (u64) (uintptr_t) job->user_cb; + + ext_and_hw_queue_submit_bd(hdev, q, ctl, len, ptr); +} + +/* + * init_signal_wait_cs - initialize a signal/wait CS + * @cs: pointer to the signal/wait CS + * + * H/W queues spinlock should be taken before calling this function + */ +static void init_signal_wait_cs(struct hl_cs *cs) +{ + struct hl_ctx *ctx = cs->ctx; + struct hl_device *hdev = ctx->hdev; + struct hl_hw_queue *hw_queue; + struct hl_cs_compl *cs_cmpl = + container_of(cs->fence, struct hl_cs_compl, base_fence); + + struct hl_hw_sob *hw_sob; + struct hl_cs_job *job; + u32 q_idx; + + /* There is only one job in a signal/wait CS */ + job = list_first_entry(&cs->job_list, struct hl_cs_job, + cs_node); + q_idx = job->hw_queue_id; + hw_queue = &hdev->kernel_queues[q_idx]; + + if (cs->type & CS_TYPE_SIGNAL) { + hw_sob = &hw_queue->hw_sob[hw_queue->curr_sob_offset]; + + cs_cmpl->hw_sob = hw_sob; + cs_cmpl->sob_val = hw_queue->next_sob_val++; + + dev_dbg(hdev->dev, + "generate signal CB, sob_id: %d, sob val: 0x%x, q_idx: %d\n", + cs_cmpl->hw_sob->sob_id, cs_cmpl->sob_val, q_idx); + + hdev->asic_funcs->gen_signal_cb(hdev, job->patched_cb, + cs_cmpl->hw_sob->sob_id); + + kref_get(&hw_sob->kref); + + /* check for wraparound */ + if (hw_queue->next_sob_val == HL_MAX_SOB_VAL) { + /* + * Decrement as we reached the max value. + * The release function won't be called here as we've + * just incremented the refcount. + */ + kref_put(&hw_sob->kref, hl_sob_reset_error); + hw_queue->next_sob_val = 1; + /* only two SOBs are currently in use */ + hw_queue->curr_sob_offset = + (hw_queue->curr_sob_offset + 1) % + HL_RSVD_SOBS_IN_USE; + + dev_dbg(hdev->dev, "switched to SOB %d, q_idx: %d\n", + hw_queue->curr_sob_offset, q_idx); + } + } else if (cs->type & CS_TYPE_WAIT) { + struct hl_cs_compl *signal_cs_cmpl; + + signal_cs_cmpl = container_of(cs->signal_fence, + struct hl_cs_compl, + base_fence); + + /* copy the the SOB id and value of the signal CS */ + cs_cmpl->hw_sob = signal_cs_cmpl->hw_sob; + cs_cmpl->sob_val = signal_cs_cmpl->sob_val; + + dev_dbg(hdev->dev, + "generate wait CB, sob_id: %d, sob_val: 0x%x, mon_id: %d, q_idx: %d\n", + cs_cmpl->hw_sob->sob_id, cs_cmpl->sob_val, + hw_queue->base_mon_id, q_idx); + + hdev->asic_funcs->gen_wait_cb(hdev, job->patched_cb, + cs_cmpl->hw_sob->sob_id, + cs_cmpl->sob_val, + hw_queue->base_mon_id, + q_idx); + + kref_get(&cs_cmpl->hw_sob->kref); + /* + * Must put the signal fence after the SOB refcnt increment so + * the SOB refcnt won't turn 0 and reset the SOB before the + * wait CS was submitted. + */ + mb(); + dma_fence_put(cs->signal_fence); + cs->signal_fence = NULL; + } +} + +/* + * hl_hw_queue_schedule_cs - schedule a command submission + * @cs: pointer to the CS + */ +int hl_hw_queue_schedule_cs(struct hl_cs *cs) +{ + struct hl_ctx *ctx = cs->ctx; + struct hl_device *hdev = ctx->hdev; + struct hl_cs_job *job, *tmp; + struct hl_hw_queue *q; + u32 max_queues; + int rc = 0, i, cq_cnt; + + hdev->asic_funcs->hw_queues_lock(hdev); + + if (hl_device_disabled_or_in_reset(hdev)) { + ctx->cs_counters.device_in_reset_drop_cnt++; + dev_err(hdev->dev, + "device is disabled or in reset, CS rejected!\n"); + rc = -EPERM; + goto out; + } + + max_queues = hdev->asic_prop.max_queues; + + q = &hdev->kernel_queues[0]; + for (i = 0, cq_cnt = 0 ; i < max_queues ; i++, q++) { + if (cs->jobs_in_queue_cnt[i]) { + switch (q->queue_type) { + case QUEUE_TYPE_EXT: + rc = ext_queue_sanity_checks(hdev, q, + cs->jobs_in_queue_cnt[i], true); + break; + case QUEUE_TYPE_INT: + rc = int_queue_sanity_checks(hdev, q, + cs->jobs_in_queue_cnt[i]); + break; + case QUEUE_TYPE_HW: + rc = hw_queue_sanity_checks(hdev, q, + cs->jobs_in_queue_cnt[i]); + break; + default: + dev_err(hdev->dev, "Queue type %d is invalid\n", + q->queue_type); + rc = -EINVAL; + break; + } + + if (rc) { + ctx->cs_counters.queue_full_drop_cnt++; + goto unroll_cq_resv; + } + + if (q->queue_type == QUEUE_TYPE_EXT) + cq_cnt++; + } + } + + if ((cs->type == CS_TYPE_SIGNAL) || (cs->type == CS_TYPE_WAIT)) + init_signal_wait_cs(cs); + + spin_lock(&hdev->hw_queues_mirror_lock); + list_add_tail(&cs->mirror_node, &hdev->hw_queues_mirror_list); + + /* Queue TDR if the CS is the first entry and if timeout is wanted */ + if ((hdev->timeout_jiffies != MAX_SCHEDULE_TIMEOUT) && + (list_first_entry(&hdev->hw_queues_mirror_list, + struct hl_cs, mirror_node) == cs)) { + cs->tdr_active = true; + schedule_delayed_work(&cs->work_tdr, hdev->timeout_jiffies); + spin_unlock(&hdev->hw_queues_mirror_lock); + } else { + spin_unlock(&hdev->hw_queues_mirror_lock); + } + + if (!hdev->cs_active_cnt++) { + struct hl_device_idle_busy_ts *ts; + + ts = &hdev->idle_busy_ts_arr[hdev->idle_busy_ts_idx]; + ts->busy_to_idle_ts = ktime_set(0, 0); + ts->idle_to_busy_ts = ktime_get(); + } + + list_for_each_entry_safe(job, tmp, &cs->job_list, cs_node) + switch (job->queue_type) { + case QUEUE_TYPE_EXT: + ext_queue_schedule_job(job); + break; + case QUEUE_TYPE_INT: + int_queue_schedule_job(job); + break; + case QUEUE_TYPE_HW: + hw_queue_schedule_job(job); + break; + default: + break; + } + + cs->submitted = true; + + goto out; + +unroll_cq_resv: + q = &hdev->kernel_queues[0]; + for (i = 0 ; (i < max_queues) && (cq_cnt > 0) ; i++, q++) { + if ((q->queue_type == QUEUE_TYPE_EXT) && + (cs->jobs_in_queue_cnt[i])) { + atomic_t *free_slots = + &hdev->completion_queue[i].free_slots_cnt; + atomic_add(cs->jobs_in_queue_cnt[i], free_slots); + cq_cnt--; + } + } + +out: + hdev->asic_funcs->hw_queues_unlock(hdev); + + return rc; +} + +/* + * hl_hw_queue_inc_ci_kernel - increment ci for kernel's queue + * + * @hdev: pointer to hl_device structure + * @hw_queue_id: which queue to increment its ci + */ +void hl_hw_queue_inc_ci_kernel(struct hl_device *hdev, u32 hw_queue_id) +{ + struct hl_hw_queue *q = &hdev->kernel_queues[hw_queue_id]; + + atomic_inc(&q->ci); +} + +static int ext_and_cpu_queue_init(struct hl_device *hdev, struct hl_hw_queue *q, + bool is_cpu_queue) +{ + void *p; + int rc; + + if (is_cpu_queue) + p = hdev->asic_funcs->cpu_accessible_dma_pool_alloc(hdev, + HL_QUEUE_SIZE_IN_BYTES, + &q->bus_address); + else + p = hdev->asic_funcs->asic_dma_alloc_coherent(hdev, + HL_QUEUE_SIZE_IN_BYTES, + &q->bus_address, + GFP_KERNEL | __GFP_ZERO); + if (!p) + return -ENOMEM; + + q->kernel_address = (u64) (uintptr_t) p; + + q->shadow_queue = kmalloc_array(HL_QUEUE_LENGTH, + sizeof(*q->shadow_queue), + GFP_KERNEL); + if (!q->shadow_queue) { + dev_err(hdev->dev, + "Failed to allocate shadow queue for H/W queue %d\n", + q->hw_queue_id); + rc = -ENOMEM; + goto free_queue; + } + + /* Make sure read/write pointers are initialized to start of queue */ + atomic_set(&q->ci, 0); + q->pi = 0; + + return 0; + +free_queue: + if (is_cpu_queue) + hdev->asic_funcs->cpu_accessible_dma_pool_free(hdev, + HL_QUEUE_SIZE_IN_BYTES, + (void *) (uintptr_t) q->kernel_address); + else + hdev->asic_funcs->asic_dma_free_coherent(hdev, + HL_QUEUE_SIZE_IN_BYTES, + (void *) (uintptr_t) q->kernel_address, + q->bus_address); + + return rc; +} + +static int int_queue_init(struct hl_device *hdev, struct hl_hw_queue *q) +{ + void *p; + + p = hdev->asic_funcs->get_int_queue_base(hdev, q->hw_queue_id, + &q->bus_address, &q->int_queue_len); + if (!p) { + dev_err(hdev->dev, + "Failed to get base address for internal queue %d\n", + q->hw_queue_id); + return -EFAULT; + } + + q->kernel_address = (u64) (uintptr_t) p; + q->pi = 0; + atomic_set(&q->ci, 0); + + return 0; +} + +static int cpu_queue_init(struct hl_device *hdev, struct hl_hw_queue *q) +{ + return ext_and_cpu_queue_init(hdev, q, true); +} + +static int ext_queue_init(struct hl_device *hdev, struct hl_hw_queue *q) +{ + return ext_and_cpu_queue_init(hdev, q, false); +} + +static int hw_queue_init(struct hl_device *hdev, struct hl_hw_queue *q) +{ + void *p; + + p = hdev->asic_funcs->asic_dma_alloc_coherent(hdev, + HL_QUEUE_SIZE_IN_BYTES, + &q->bus_address, + GFP_KERNEL | __GFP_ZERO); + if (!p) + return -ENOMEM; + + q->kernel_address = (u64) (uintptr_t) p; + + /* Make sure read/write pointers are initialized to start of queue */ + atomic_set(&q->ci, 0); + q->pi = 0; + + return 0; +} + +static void sync_stream_queue_init(struct hl_device *hdev, u32 q_idx) +{ + struct hl_hw_queue *hw_queue = &hdev->kernel_queues[q_idx]; + struct asic_fixed_properties *prop = &hdev->asic_prop; + struct hl_hw_sob *hw_sob; + int sob, queue_idx = hdev->sync_stream_queue_idx++; + + hw_queue->base_sob_id = + prop->sync_stream_first_sob + queue_idx * HL_RSVD_SOBS; + hw_queue->base_mon_id = + prop->sync_stream_first_mon + queue_idx * HL_RSVD_MONS; + hw_queue->next_sob_val = 1; + hw_queue->curr_sob_offset = 0; + + for (sob = 0 ; sob < HL_RSVD_SOBS ; sob++) { + hw_sob = &hw_queue->hw_sob[sob]; + hw_sob->hdev = hdev; + hw_sob->sob_id = hw_queue->base_sob_id + sob; + hw_sob->q_idx = q_idx; + kref_init(&hw_sob->kref); + } +} + +static void sync_stream_queue_reset(struct hl_device *hdev, u32 q_idx) +{ + struct hl_hw_queue *hw_queue = &hdev->kernel_queues[q_idx]; + + /* + * In case we got here due to a stuck CS, the refcnt might be bigger + * than 1 and therefore we reset it. + */ + kref_init(&hw_queue->hw_sob[hw_queue->curr_sob_offset].kref); + hw_queue->curr_sob_offset = 0; + hw_queue->next_sob_val = 1; +} + +/* + * queue_init - main initialization function for H/W queue object + * + * @hdev: pointer to hl_device device structure + * @q: pointer to hl_hw_queue queue structure + * @hw_queue_id: The id of the H/W queue + * + * Allocate dma-able memory for the queue and initialize fields + * Returns 0 on success + */ +static int queue_init(struct hl_device *hdev, struct hl_hw_queue *q, + u32 hw_queue_id) +{ + int rc; + + q->hw_queue_id = hw_queue_id; + + switch (q->queue_type) { + case QUEUE_TYPE_EXT: + rc = ext_queue_init(hdev, q); + break; + case QUEUE_TYPE_INT: + rc = int_queue_init(hdev, q); + break; + case QUEUE_TYPE_CPU: + rc = cpu_queue_init(hdev, q); + break; + case QUEUE_TYPE_HW: + rc = hw_queue_init(hdev, q); + break; + case QUEUE_TYPE_NA: + q->valid = 0; + return 0; + default: + dev_crit(hdev->dev, "wrong queue type %d during init\n", + q->queue_type); + rc = -EINVAL; + break; + } + + if (q->supports_sync_stream) + sync_stream_queue_init(hdev, q->hw_queue_id); + + if (rc) + return rc; + + q->valid = 1; + + return 0; +} + +/* + * hw_queue_fini - destroy queue + * + * @hdev: pointer to hl_device device structure + * @q: pointer to hl_hw_queue queue structure + * + * Free the queue memory + */ +static void queue_fini(struct hl_device *hdev, struct hl_hw_queue *q) +{ + if (!q->valid) + return; + + /* + * If we arrived here, there are no jobs waiting on this queue + * so we can safely remove it. + * This is because this function can only called when: + * 1. Either a context is deleted, which only can occur if all its + * jobs were finished + * 2. A context wasn't able to be created due to failure or timeout, + * which means there are no jobs on the queue yet + * + * The only exception are the queues of the kernel context, but + * if they are being destroyed, it means that the entire module is + * being removed. If the module is removed, it means there is no open + * user context. It also means that if a job was submitted by + * the kernel driver (e.g. context creation), the job itself was + * released by the kernel driver when a timeout occurred on its + * Completion. Thus, we don't need to release it again. + */ + + if (q->queue_type == QUEUE_TYPE_INT) + return; + + kfree(q->shadow_queue); + + if (q->queue_type == QUEUE_TYPE_CPU) + hdev->asic_funcs->cpu_accessible_dma_pool_free(hdev, + HL_QUEUE_SIZE_IN_BYTES, + (void *) (uintptr_t) q->kernel_address); + else + hdev->asic_funcs->asic_dma_free_coherent(hdev, + HL_QUEUE_SIZE_IN_BYTES, + (void *) (uintptr_t) q->kernel_address, + q->bus_address); +} + +int hl_hw_queues_create(struct hl_device *hdev) +{ + struct asic_fixed_properties *asic = &hdev->asic_prop; + struct hl_hw_queue *q; + int i, rc, q_ready_cnt; + + hdev->kernel_queues = kcalloc(asic->max_queues, + sizeof(*hdev->kernel_queues), GFP_KERNEL); + + if (!hdev->kernel_queues) { + dev_err(hdev->dev, "Not enough memory for H/W queues\n"); + return -ENOMEM; + } + + /* Initialize the H/W queues */ + for (i = 0, q_ready_cnt = 0, q = hdev->kernel_queues; + i < asic->max_queues ; i++, q_ready_cnt++, q++) { + + q->queue_type = asic->hw_queues_props[i].type; + q->supports_sync_stream = + asic->hw_queues_props[i].supports_sync_stream; + rc = queue_init(hdev, q, i); + if (rc) { + dev_err(hdev->dev, + "failed to initialize queue %d\n", i); + goto release_queues; + } + } + + return 0; + +release_queues: + for (i = 0, q = hdev->kernel_queues ; i < q_ready_cnt ; i++, q++) + queue_fini(hdev, q); + + kfree(hdev->kernel_queues); + + return rc; +} + +void hl_hw_queues_destroy(struct hl_device *hdev) +{ + struct hl_hw_queue *q; + u32 max_queues = hdev->asic_prop.max_queues; + int i; + + for (i = 0, q = hdev->kernel_queues ; i < max_queues ; i++, q++) + queue_fini(hdev, q); + + kfree(hdev->kernel_queues); +} + +void hl_hw_queue_reset(struct hl_device *hdev, bool hard_reset) +{ + struct hl_hw_queue *q; + u32 max_queues = hdev->asic_prop.max_queues; + int i; + + for (i = 0, q = hdev->kernel_queues ; i < max_queues ; i++, q++) { + if ((!q->valid) || + ((!hard_reset) && (q->queue_type == QUEUE_TYPE_CPU))) + continue; + q->pi = 0; + atomic_set(&q->ci, 0); + + if (q->supports_sync_stream) + sync_stream_queue_reset(hdev, q->hw_queue_id); + } +} diff --git a/drivers/misc/habanalabs/common/hwmon.c b/drivers/misc/habanalabs/common/hwmon.c new file mode 100644 index 000000000000..8c6cd77e6af6 --- /dev/null +++ b/drivers/misc/habanalabs/common/hwmon.c @@ -0,0 +1,579 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright 2016-2019 HabanaLabs, Ltd. + * All Rights Reserved. + */ + +#include "habanalabs.h" + +#include +#include + +#define SENSORS_PKT_TIMEOUT 1000000 /* 1s */ +#define HWMON_NR_SENSOR_TYPES (hwmon_pwm + 1) + +int hl_build_hwmon_channel_info(struct hl_device *hdev, + struct armcp_sensor *sensors_arr) +{ + u32 counts[HWMON_NR_SENSOR_TYPES] = {0}; + u32 *sensors_by_type[HWMON_NR_SENSOR_TYPES] = {NULL}; + u32 sensors_by_type_next_index[HWMON_NR_SENSOR_TYPES] = {0}; + struct hwmon_channel_info **channels_info; + u32 num_sensors_for_type, num_active_sensor_types = 0, + arr_size = 0, *curr_arr; + enum hwmon_sensor_types type; + int rc, i, j; + + for (i = 0 ; i < ARMCP_MAX_SENSORS ; i++) { + type = le32_to_cpu(sensors_arr[i].type); + + if ((type == 0) && (sensors_arr[i].flags == 0)) + break; + + if (type >= HWMON_NR_SENSOR_TYPES) { + dev_err(hdev->dev, + "Got wrong sensor type %d from device\n", type); + return -EINVAL; + } + + counts[type]++; + arr_size++; + } + + for (i = 0 ; i < HWMON_NR_SENSOR_TYPES ; i++) { + if (counts[i] == 0) + continue; + + num_sensors_for_type = counts[i] + 1; + curr_arr = kcalloc(num_sensors_for_type, sizeof(*curr_arr), + GFP_KERNEL); + if (!curr_arr) { + rc = -ENOMEM; + goto sensors_type_err; + } + + num_active_sensor_types++; + sensors_by_type[i] = curr_arr; + } + + for (i = 0 ; i < arr_size ; i++) { + type = le32_to_cpu(sensors_arr[i].type); + curr_arr = sensors_by_type[type]; + curr_arr[sensors_by_type_next_index[type]++] = + le32_to_cpu(sensors_arr[i].flags); + } + + channels_info = kcalloc(num_active_sensor_types + 1, + sizeof(*channels_info), GFP_KERNEL); + if (!channels_info) { + rc = -ENOMEM; + goto channels_info_array_err; + } + + for (i = 0 ; i < num_active_sensor_types ; i++) { + channels_info[i] = kzalloc(sizeof(*channels_info[i]), + GFP_KERNEL); + if (!channels_info[i]) { + rc = -ENOMEM; + goto channel_info_err; + } + } + + for (i = 0, j = 0 ; i < HWMON_NR_SENSOR_TYPES ; i++) { + if (!sensors_by_type[i]) + continue; + + channels_info[j]->type = i; + channels_info[j]->config = sensors_by_type[i]; + j++; + } + + hdev->hl_chip_info->info = + (const struct hwmon_channel_info **)channels_info; + + return 0; + +channel_info_err: + for (i = 0 ; i < num_active_sensor_types ; i++) + if (channels_info[i]) { + kfree(channels_info[i]->config); + kfree(channels_info[i]); + } + kfree(channels_info); +channels_info_array_err: +sensors_type_err: + for (i = 0 ; i < HWMON_NR_SENSOR_TYPES ; i++) + kfree(sensors_by_type[i]); + + return rc; +} + +static int hl_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct hl_device *hdev = dev_get_drvdata(dev); + int rc; + + if (hl_device_disabled_or_in_reset(hdev)) + return -ENODEV; + + switch (type) { + case hwmon_temp: + switch (attr) { + case hwmon_temp_input: + case hwmon_temp_max: + case hwmon_temp_crit: + case hwmon_temp_max_hyst: + case hwmon_temp_crit_hyst: + case hwmon_temp_offset: + case hwmon_temp_highest: + break; + default: + return -EINVAL; + } + + rc = hl_get_temperature(hdev, channel, attr, val); + break; + case hwmon_in: + switch (attr) { + case hwmon_in_input: + case hwmon_in_min: + case hwmon_in_max: + case hwmon_in_highest: + break; + default: + return -EINVAL; + } + + rc = hl_get_voltage(hdev, channel, attr, val); + break; + case hwmon_curr: + switch (attr) { + case hwmon_curr_input: + case hwmon_curr_min: + case hwmon_curr_max: + case hwmon_curr_highest: + break; + default: + return -EINVAL; + } + + rc = hl_get_current(hdev, channel, attr, val); + break; + case hwmon_fan: + switch (attr) { + case hwmon_fan_input: + case hwmon_fan_min: + case hwmon_fan_max: + break; + default: + return -EINVAL; + } + rc = hl_get_fan_speed(hdev, channel, attr, val); + break; + case hwmon_pwm: + switch (attr) { + case hwmon_pwm_input: + case hwmon_pwm_enable: + break; + default: + return -EINVAL; + } + rc = hl_get_pwm_info(hdev, channel, attr, val); + break; + default: + return -EINVAL; + } + return rc; +} + +static int hl_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + struct hl_device *hdev = dev_get_drvdata(dev); + + if (hl_device_disabled_or_in_reset(hdev)) + return -ENODEV; + + switch (type) { + case hwmon_temp: + switch (attr) { + case hwmon_temp_offset: + case hwmon_temp_reset_history: + break; + default: + return -EINVAL; + } + hl_set_temperature(hdev, channel, attr, val); + break; + case hwmon_pwm: + switch (attr) { + case hwmon_pwm_input: + case hwmon_pwm_enable: + break; + default: + return -EINVAL; + } + hl_set_pwm_info(hdev, channel, attr, val); + break; + case hwmon_in: + switch (attr) { + case hwmon_in_reset_history: + break; + default: + return -EINVAL; + } + hl_set_voltage(hdev, channel, attr, val); + break; + case hwmon_curr: + switch (attr) { + case hwmon_curr_reset_history: + break; + default: + return -EINVAL; + } + hl_set_current(hdev, channel, attr, val); + break; + default: + return -EINVAL; + } + return 0; +} + +static umode_t hl_is_visible(const void *data, enum hwmon_sensor_types type, + u32 attr, int channel) +{ + switch (type) { + case hwmon_temp: + switch (attr) { + case hwmon_temp_input: + case hwmon_temp_max: + case hwmon_temp_max_hyst: + case hwmon_temp_crit: + case hwmon_temp_crit_hyst: + case hwmon_temp_highest: + return 0444; + case hwmon_temp_offset: + return 0644; + case hwmon_temp_reset_history: + return 0200; + } + break; + case hwmon_in: + switch (attr) { + case hwmon_in_input: + case hwmon_in_min: + case hwmon_in_max: + case hwmon_in_highest: + return 0444; + case hwmon_in_reset_history: + return 0200; + } + break; + case hwmon_curr: + switch (attr) { + case hwmon_curr_input: + case hwmon_curr_min: + case hwmon_curr_max: + case hwmon_curr_highest: + return 0444; + case hwmon_curr_reset_history: + return 0200; + } + break; + case hwmon_fan: + switch (attr) { + case hwmon_fan_input: + case hwmon_fan_min: + case hwmon_fan_max: + return 0444; + } + break; + case hwmon_pwm: + switch (attr) { + case hwmon_pwm_input: + case hwmon_pwm_enable: + return 0644; + } + break; + default: + break; + } + return 0; +} + +static const struct hwmon_ops hl_hwmon_ops = { + .is_visible = hl_is_visible, + .read = hl_read, + .write = hl_write +}; + +int hl_get_temperature(struct hl_device *hdev, + int sensor_index, u32 attr, long *value) +{ + struct armcp_packet pkt; + int rc; + + memset(&pkt, 0, sizeof(pkt)); + + pkt.ctl = cpu_to_le32(ARMCP_PACKET_TEMPERATURE_GET << + ARMCP_PKT_CTL_OPCODE_SHIFT); + pkt.sensor_index = __cpu_to_le16(sensor_index); + pkt.type = __cpu_to_le16(attr); + + rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), + SENSORS_PKT_TIMEOUT, value); + + if (rc) { + dev_err(hdev->dev, + "Failed to get temperature from sensor %d, error %d\n", + sensor_index, rc); + *value = 0; + } + + return rc; +} + +int hl_set_temperature(struct hl_device *hdev, + int sensor_index, u32 attr, long value) +{ + struct armcp_packet pkt; + int rc; + + memset(&pkt, 0, sizeof(pkt)); + + pkt.ctl = cpu_to_le32(ARMCP_PACKET_TEMPERATURE_SET << + ARMCP_PKT_CTL_OPCODE_SHIFT); + pkt.sensor_index = __cpu_to_le16(sensor_index); + pkt.type = __cpu_to_le16(attr); + pkt.value = __cpu_to_le64(value); + + rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), + SENSORS_PKT_TIMEOUT, NULL); + + if (rc) + dev_err(hdev->dev, + "Failed to set temperature of sensor %d, error %d\n", + sensor_index, rc); + + return rc; +} + +int hl_get_voltage(struct hl_device *hdev, + int sensor_index, u32 attr, long *value) +{ + struct armcp_packet pkt; + int rc; + + memset(&pkt, 0, sizeof(pkt)); + + pkt.ctl = cpu_to_le32(ARMCP_PACKET_VOLTAGE_GET << + ARMCP_PKT_CTL_OPCODE_SHIFT); + pkt.sensor_index = __cpu_to_le16(sensor_index); + pkt.type = __cpu_to_le16(attr); + + rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), + SENSORS_PKT_TIMEOUT, value); + + if (rc) { + dev_err(hdev->dev, + "Failed to get voltage from sensor %d, error %d\n", + sensor_index, rc); + *value = 0; + } + + return rc; +} + +int hl_get_current(struct hl_device *hdev, + int sensor_index, u32 attr, long *value) +{ + struct armcp_packet pkt; + int rc; + + memset(&pkt, 0, sizeof(pkt)); + + pkt.ctl = cpu_to_le32(ARMCP_PACKET_CURRENT_GET << + ARMCP_PKT_CTL_OPCODE_SHIFT); + pkt.sensor_index = __cpu_to_le16(sensor_index); + pkt.type = __cpu_to_le16(attr); + + rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), + SENSORS_PKT_TIMEOUT, value); + + if (rc) { + dev_err(hdev->dev, + "Failed to get current from sensor %d, error %d\n", + sensor_index, rc); + *value = 0; + } + + return rc; +} + +int hl_get_fan_speed(struct hl_device *hdev, + int sensor_index, u32 attr, long *value) +{ + struct armcp_packet pkt; + int rc; + + memset(&pkt, 0, sizeof(pkt)); + + pkt.ctl = cpu_to_le32(ARMCP_PACKET_FAN_SPEED_GET << + ARMCP_PKT_CTL_OPCODE_SHIFT); + pkt.sensor_index = __cpu_to_le16(sensor_index); + pkt.type = __cpu_to_le16(attr); + + rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), + SENSORS_PKT_TIMEOUT, value); + + if (rc) { + dev_err(hdev->dev, + "Failed to get fan speed from sensor %d, error %d\n", + sensor_index, rc); + *value = 0; + } + + return rc; +} + +int hl_get_pwm_info(struct hl_device *hdev, + int sensor_index, u32 attr, long *value) +{ + struct armcp_packet pkt; + int rc; + + memset(&pkt, 0, sizeof(pkt)); + + pkt.ctl = cpu_to_le32(ARMCP_PACKET_PWM_GET << + ARMCP_PKT_CTL_OPCODE_SHIFT); + pkt.sensor_index = __cpu_to_le16(sensor_index); + pkt.type = __cpu_to_le16(attr); + + rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), + SENSORS_PKT_TIMEOUT, value); + + if (rc) { + dev_err(hdev->dev, + "Failed to get pwm info from sensor %d, error %d\n", + sensor_index, rc); + *value = 0; + } + + return rc; +} + +void hl_set_pwm_info(struct hl_device *hdev, int sensor_index, u32 attr, + long value) +{ + struct armcp_packet pkt; + int rc; + + memset(&pkt, 0, sizeof(pkt)); + + pkt.ctl = cpu_to_le32(ARMCP_PACKET_PWM_SET << + ARMCP_PKT_CTL_OPCODE_SHIFT); + pkt.sensor_index = __cpu_to_le16(sensor_index); + pkt.type = __cpu_to_le16(attr); + pkt.value = cpu_to_le64(value); + + rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), + SENSORS_PKT_TIMEOUT, NULL); + + if (rc) + dev_err(hdev->dev, + "Failed to set pwm info to sensor %d, error %d\n", + sensor_index, rc); +} + +int hl_set_voltage(struct hl_device *hdev, + int sensor_index, u32 attr, long value) +{ + struct armcp_packet pkt; + int rc; + + memset(&pkt, 0, sizeof(pkt)); + + pkt.ctl = cpu_to_le32(ARMCP_PACKET_VOLTAGE_SET << + ARMCP_PKT_CTL_OPCODE_SHIFT); + pkt.sensor_index = __cpu_to_le16(sensor_index); + pkt.type = __cpu_to_le16(attr); + pkt.value = __cpu_to_le64(value); + + rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), + SENSORS_PKT_TIMEOUT, NULL); + + if (rc) + dev_err(hdev->dev, + "Failed to set voltage of sensor %d, error %d\n", + sensor_index, rc); + + return rc; +} + +int hl_set_current(struct hl_device *hdev, + int sensor_index, u32 attr, long value) +{ + struct armcp_packet pkt; + int rc; + + memset(&pkt, 0, sizeof(pkt)); + + pkt.ctl = cpu_to_le32(ARMCP_PACKET_CURRENT_SET << + ARMCP_PKT_CTL_OPCODE_SHIFT); + pkt.sensor_index = __cpu_to_le16(sensor_index); + pkt.type = __cpu_to_le16(attr); + pkt.value = __cpu_to_le64(value); + + rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), + SENSORS_PKT_TIMEOUT, NULL); + + if (rc) + dev_err(hdev->dev, + "Failed to set current of sensor %d, error %d\n", + sensor_index, rc); + + return rc; +} + +int hl_hwmon_init(struct hl_device *hdev) +{ + struct device *dev = hdev->pdev ? &hdev->pdev->dev : hdev->dev; + struct asic_fixed_properties *prop = &hdev->asic_prop; + int rc; + + if ((hdev->hwmon_initialized) || !(hdev->fw_loading)) + return 0; + + if (hdev->hl_chip_info->info) { + hdev->hl_chip_info->ops = &hl_hwmon_ops; + + hdev->hwmon_dev = hwmon_device_register_with_info(dev, + prop->armcp_info.card_name, hdev, + hdev->hl_chip_info, NULL); + if (IS_ERR(hdev->hwmon_dev)) { + rc = PTR_ERR(hdev->hwmon_dev); + dev_err(hdev->dev, + "Unable to register hwmon device: %d\n", rc); + return rc; + } + + dev_info(hdev->dev, "%s: add sensors information\n", + dev_name(hdev->hwmon_dev)); + + hdev->hwmon_initialized = true; + } else { + dev_info(hdev->dev, "no available sensors\n"); + } + + return 0; +} + +void hl_hwmon_fini(struct hl_device *hdev) +{ + if (!hdev->hwmon_initialized) + return; + + hwmon_device_unregister(hdev->hwmon_dev); +} diff --git a/drivers/misc/habanalabs/common/irq.c b/drivers/misc/habanalabs/common/irq.c new file mode 100644 index 000000000000..c8db717023f5 --- /dev/null +++ b/drivers/misc/habanalabs/common/irq.c @@ -0,0 +1,320 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright 2016-2019 HabanaLabs, Ltd. + * All Rights Reserved. + */ + +#include "habanalabs.h" + +#include + +/** + * struct hl_eqe_work - This structure is used to schedule work of EQ + * entry and armcp_reset event + * + * @eq_work: workqueue object to run when EQ entry is received + * @hdev: pointer to device structure + * @eq_entry: copy of the EQ entry + */ +struct hl_eqe_work { + struct work_struct eq_work; + struct hl_device *hdev; + struct hl_eq_entry eq_entry; +}; + +/** + * hl_cq_inc_ptr - increment ci or pi of cq + * + * @ptr: the current ci or pi value of the completion queue + * + * Increment ptr by 1. If it reaches the number of completion queue + * entries, set it to 0 + */ +inline u32 hl_cq_inc_ptr(u32 ptr) +{ + ptr++; + if (unlikely(ptr == HL_CQ_LENGTH)) + ptr = 0; + return ptr; +} + +/** + * hl_eq_inc_ptr - increment ci of eq + * + * @ptr: the current ci value of the event queue + * + * Increment ptr by 1. If it reaches the number of event queue + * entries, set it to 0 + */ +inline u32 hl_eq_inc_ptr(u32 ptr) +{ + ptr++; + if (unlikely(ptr == HL_EQ_LENGTH)) + ptr = 0; + return ptr; +} + +static void irq_handle_eqe(struct work_struct *work) +{ + struct hl_eqe_work *eqe_work = container_of(work, struct hl_eqe_work, + eq_work); + struct hl_device *hdev = eqe_work->hdev; + + hdev->asic_funcs->handle_eqe(hdev, &eqe_work->eq_entry); + + kfree(eqe_work); +} + +/** + * hl_irq_handler_cq - irq handler for completion queue + * + * @irq: irq number + * @arg: pointer to completion queue structure + * + */ +irqreturn_t hl_irq_handler_cq(int irq, void *arg) +{ + struct hl_cq *cq = arg; + struct hl_device *hdev = cq->hdev; + struct hl_hw_queue *queue; + struct hl_cs_job *job; + bool shadow_index_valid; + u16 shadow_index; + struct hl_cq_entry *cq_entry, *cq_base; + + if (hdev->disabled) { + dev_dbg(hdev->dev, + "Device disabled but received IRQ %d for CQ %d\n", + irq, cq->hw_queue_id); + return IRQ_HANDLED; + } + + cq_base = (struct hl_cq_entry *) (uintptr_t) cq->kernel_address; + + while (1) { + bool entry_ready = ((le32_to_cpu(cq_base[cq->ci].data) & + CQ_ENTRY_READY_MASK) + >> CQ_ENTRY_READY_SHIFT); + + if (!entry_ready) + break; + + cq_entry = (struct hl_cq_entry *) &cq_base[cq->ci]; + + /* Make sure we read CQ entry contents after we've + * checked the ownership bit. + */ + dma_rmb(); + + shadow_index_valid = ((le32_to_cpu(cq_entry->data) & + CQ_ENTRY_SHADOW_INDEX_VALID_MASK) + >> CQ_ENTRY_SHADOW_INDEX_VALID_SHIFT); + + shadow_index = (u16) ((le32_to_cpu(cq_entry->data) & + CQ_ENTRY_SHADOW_INDEX_MASK) + >> CQ_ENTRY_SHADOW_INDEX_SHIFT); + + queue = &hdev->kernel_queues[cq->hw_queue_id]; + + if ((shadow_index_valid) && (!hdev->disabled)) { + job = queue->shadow_queue[hl_pi_2_offset(shadow_index)]; + queue_work(hdev->cq_wq[cq->cq_idx], &job->finish_work); + } + + atomic_inc(&queue->ci); + + /* Clear CQ entry ready bit */ + cq_entry->data = cpu_to_le32(le32_to_cpu(cq_entry->data) & + ~CQ_ENTRY_READY_MASK); + + cq->ci = hl_cq_inc_ptr(cq->ci); + + /* Increment free slots */ + atomic_inc(&cq->free_slots_cnt); + } + + return IRQ_HANDLED; +} + +/** + * hl_irq_handler_eq - irq handler for event queue + * + * @irq: irq number + * @arg: pointer to event queue structure + * + */ +irqreturn_t hl_irq_handler_eq(int irq, void *arg) +{ + struct hl_eq *eq = arg; + struct hl_device *hdev = eq->hdev; + struct hl_eq_entry *eq_entry; + struct hl_eq_entry *eq_base; + struct hl_eqe_work *handle_eqe_work; + + eq_base = (struct hl_eq_entry *) (uintptr_t) eq->kernel_address; + + while (1) { + bool entry_ready = + ((le32_to_cpu(eq_base[eq->ci].hdr.ctl) & + EQ_CTL_READY_MASK) >> EQ_CTL_READY_SHIFT); + + if (!entry_ready) + break; + + eq_entry = &eq_base[eq->ci]; + + /* + * Make sure we read EQ entry contents after we've + * checked the ownership bit. + */ + dma_rmb(); + + if (hdev->disabled) { + dev_warn(hdev->dev, + "Device disabled but received IRQ %d for EQ\n", + irq); + goto skip_irq; + } + + handle_eqe_work = kmalloc(sizeof(*handle_eqe_work), GFP_ATOMIC); + if (handle_eqe_work) { + INIT_WORK(&handle_eqe_work->eq_work, irq_handle_eqe); + handle_eqe_work->hdev = hdev; + + memcpy(&handle_eqe_work->eq_entry, eq_entry, + sizeof(*eq_entry)); + + queue_work(hdev->eq_wq, &handle_eqe_work->eq_work); + } +skip_irq: + /* Clear EQ entry ready bit */ + eq_entry->hdr.ctl = + cpu_to_le32(le32_to_cpu(eq_entry->hdr.ctl) & + ~EQ_CTL_READY_MASK); + + eq->ci = hl_eq_inc_ptr(eq->ci); + + hdev->asic_funcs->update_eq_ci(hdev, eq->ci); + } + + return IRQ_HANDLED; +} + +/** + * hl_cq_init - main initialization function for an cq object + * + * @hdev: pointer to device structure + * @q: pointer to cq structure + * @hw_queue_id: The H/W queue ID this completion queue belongs to + * + * Allocate dma-able memory for the completion queue and initialize fields + * Returns 0 on success + */ +int hl_cq_init(struct hl_device *hdev, struct hl_cq *q, u32 hw_queue_id) +{ + void *p; + + p = hdev->asic_funcs->asic_dma_alloc_coherent(hdev, HL_CQ_SIZE_IN_BYTES, + &q->bus_address, GFP_KERNEL | __GFP_ZERO); + if (!p) + return -ENOMEM; + + q->hdev = hdev; + q->kernel_address = (u64) (uintptr_t) p; + q->hw_queue_id = hw_queue_id; + q->ci = 0; + q->pi = 0; + + atomic_set(&q->free_slots_cnt, HL_CQ_LENGTH); + + return 0; +} + +/** + * hl_cq_fini - destroy completion queue + * + * @hdev: pointer to device structure + * @q: pointer to cq structure + * + * Free the completion queue memory + */ +void hl_cq_fini(struct hl_device *hdev, struct hl_cq *q) +{ + hdev->asic_funcs->asic_dma_free_coherent(hdev, HL_CQ_SIZE_IN_BYTES, + (void *) (uintptr_t) q->kernel_address, q->bus_address); +} + +void hl_cq_reset(struct hl_device *hdev, struct hl_cq *q) +{ + q->ci = 0; + q->pi = 0; + + atomic_set(&q->free_slots_cnt, HL_CQ_LENGTH); + + /* + * It's not enough to just reset the PI/CI because the H/W may have + * written valid completion entries before it was halted and therefore + * we need to clean the actual queues so we won't process old entries + * when the device is operational again + */ + + memset((void *) (uintptr_t) q->kernel_address, 0, HL_CQ_SIZE_IN_BYTES); +} + +/** + * hl_eq_init - main initialization function for an event queue object + * + * @hdev: pointer to device structure + * @q: pointer to eq structure + * + * Allocate dma-able memory for the event queue and initialize fields + * Returns 0 on success + */ +int hl_eq_init(struct hl_device *hdev, struct hl_eq *q) +{ + void *p; + + p = hdev->asic_funcs->cpu_accessible_dma_pool_alloc(hdev, + HL_EQ_SIZE_IN_BYTES, + &q->bus_address); + if (!p) + return -ENOMEM; + + q->hdev = hdev; + q->kernel_address = (u64) (uintptr_t) p; + q->ci = 0; + + return 0; +} + +/** + * hl_eq_fini - destroy event queue + * + * @hdev: pointer to device structure + * @q: pointer to eq structure + * + * Free the event queue memory + */ +void hl_eq_fini(struct hl_device *hdev, struct hl_eq *q) +{ + flush_workqueue(hdev->eq_wq); + + hdev->asic_funcs->cpu_accessible_dma_pool_free(hdev, + HL_EQ_SIZE_IN_BYTES, + (void *) (uintptr_t) q->kernel_address); +} + +void hl_eq_reset(struct hl_device *hdev, struct hl_eq *q) +{ + q->ci = 0; + + /* + * It's not enough to just reset the PI/CI because the H/W may have + * written valid completion entries before it was halted and therefore + * we need to clean the actual queues so we won't process old entries + * when the device is operational again + */ + + memset((void *) (uintptr_t) q->kernel_address, 0, HL_EQ_SIZE_IN_BYTES); +} diff --git a/drivers/misc/habanalabs/common/memory.c b/drivers/misc/habanalabs/common/memory.c new file mode 100644 index 000000000000..e4e1693e5c6c --- /dev/null +++ b/drivers/misc/habanalabs/common/memory.c @@ -0,0 +1,1843 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright 2016-2019 HabanaLabs, Ltd. + * All Rights Reserved. + */ + +#include +#include "habanalabs.h" +#include "include/hw_ip/mmu/mmu_general.h" + +#include +#include +#include + +#define HL_MMU_DEBUG 0 + +/* + * The va ranges in context object contain a list with the available chunks of + * device virtual memory. + * There is one range for host allocations and one for DRAM allocations. + * + * On initialization each range contains one chunk of all of its available + * virtual range which is a half of the total device virtual range. + * + * On each mapping of physical pages, a suitable virtual range chunk (with a + * minimum size) is selected from the list. If the chunk size equals the + * requested size, the chunk is returned. Otherwise, the chunk is split into + * two chunks - one to return as result and a remainder to stay in the list. + * + * On each Unmapping of a virtual address, the relevant virtual chunk is + * returned to the list. The chunk is added to the list and if its edges match + * the edges of the adjacent chunks (means a contiguous chunk can be created), + * the chunks are merged. + * + * On finish, the list is checked to have only one chunk of all the relevant + * virtual range (which is a half of the device total virtual range). + * If not (means not all mappings were unmapped), a warning is printed. + */ + +/* + * alloc_device_memory - allocate device memory + * + * @ctx : current context + * @args : host parameters containing the requested size + * @ret_handle : result handle + * + * This function does the following: + * - Allocate the requested size rounded up to 2MB pages + * - Return unique handle + */ +static int alloc_device_memory(struct hl_ctx *ctx, struct hl_mem_in *args, + u32 *ret_handle) +{ + struct hl_device *hdev = ctx->hdev; + struct hl_vm *vm = &hdev->vm; + struct hl_vm_phys_pg_pack *phys_pg_pack; + u64 paddr = 0, total_size, num_pgs, i; + u32 num_curr_pgs, page_size, page_shift; + int handle, rc; + bool contiguous; + + num_curr_pgs = 0; + page_size = hdev->asic_prop.dram_page_size; + page_shift = __ffs(page_size); + num_pgs = (args->alloc.mem_size + (page_size - 1)) >> page_shift; + total_size = num_pgs << page_shift; + + contiguous = args->flags & HL_MEM_CONTIGUOUS; + + if (contiguous) { + paddr = (u64) gen_pool_alloc(vm->dram_pg_pool, total_size); + if (!paddr) { + dev_err(hdev->dev, + "failed to allocate %llu huge contiguous pages\n", + num_pgs); + return -ENOMEM; + } + } + + phys_pg_pack = kzalloc(sizeof(*phys_pg_pack), GFP_KERNEL); + if (!phys_pg_pack) { + rc = -ENOMEM; + goto pages_pack_err; + } + + phys_pg_pack->vm_type = VM_TYPE_PHYS_PACK; + phys_pg_pack->asid = ctx->asid; + phys_pg_pack->npages = num_pgs; + phys_pg_pack->page_size = page_size; + phys_pg_pack->total_size = total_size; + phys_pg_pack->flags = args->flags; + phys_pg_pack->contiguous = contiguous; + + phys_pg_pack->pages = kvmalloc_array(num_pgs, sizeof(u64), GFP_KERNEL); + if (!phys_pg_pack->pages) { + rc = -ENOMEM; + goto pages_arr_err; + } + + if (phys_pg_pack->contiguous) { + for (i = 0 ; i < num_pgs ; i++) + phys_pg_pack->pages[i] = paddr + i * page_size; + } else { + for (i = 0 ; i < num_pgs ; i++) { + phys_pg_pack->pages[i] = (u64) gen_pool_alloc( + vm->dram_pg_pool, + page_size); + if (!phys_pg_pack->pages[i]) { + dev_err(hdev->dev, + "Failed to allocate device memory (out of memory)\n"); + rc = -ENOMEM; + goto page_err; + } + + num_curr_pgs++; + } + } + + spin_lock(&vm->idr_lock); + handle = idr_alloc(&vm->phys_pg_pack_handles, phys_pg_pack, 1, 0, + GFP_ATOMIC); + spin_unlock(&vm->idr_lock); + + if (handle < 0) { + dev_err(hdev->dev, "Failed to get handle for page\n"); + rc = -EFAULT; + goto idr_err; + } + + for (i = 0 ; i < num_pgs ; i++) + kref_get(&vm->dram_pg_pool_refcount); + + phys_pg_pack->handle = handle; + + atomic64_add(phys_pg_pack->total_size, &ctx->dram_phys_mem); + atomic64_add(phys_pg_pack->total_size, &hdev->dram_used_mem); + + *ret_handle = handle; + + return 0; + +idr_err: +page_err: + if (!phys_pg_pack->contiguous) + for (i = 0 ; i < num_curr_pgs ; i++) + gen_pool_free(vm->dram_pg_pool, phys_pg_pack->pages[i], + page_size); + + kvfree(phys_pg_pack->pages); +pages_arr_err: + kfree(phys_pg_pack); +pages_pack_err: + if (contiguous) + gen_pool_free(vm->dram_pg_pool, paddr, total_size); + + return rc; +} + +/* + * dma_map_host_va - DMA mapping of the given host virtual address. + * @hdev: habanalabs device structure + * @addr: the host virtual address of the memory area + * @size: the size of the memory area + * @p_userptr: pointer to result userptr structure + * + * This function does the following: + * - Allocate userptr structure + * - Pin the given host memory using the userptr structure + * - Perform DMA mapping to have the DMA addresses of the pages + */ +static int dma_map_host_va(struct hl_device *hdev, u64 addr, u64 size, + struct hl_userptr **p_userptr) +{ + struct hl_userptr *userptr; + int rc; + + userptr = kzalloc(sizeof(*userptr), GFP_KERNEL); + if (!userptr) { + rc = -ENOMEM; + goto userptr_err; + } + + rc = hl_pin_host_memory(hdev, addr, size, userptr); + if (rc) { + dev_err(hdev->dev, "Failed to pin host memory\n"); + goto pin_err; + } + + rc = hdev->asic_funcs->asic_dma_map_sg(hdev, userptr->sgt->sgl, + userptr->sgt->nents, DMA_BIDIRECTIONAL); + if (rc) { + dev_err(hdev->dev, "failed to map sgt with DMA region\n"); + goto dma_map_err; + } + + userptr->dma_mapped = true; + userptr->dir = DMA_BIDIRECTIONAL; + userptr->vm_type = VM_TYPE_USERPTR; + + *p_userptr = userptr; + + return 0; + +dma_map_err: + hl_unpin_host_memory(hdev, userptr); +pin_err: + kfree(userptr); +userptr_err: + + return rc; +} + +/* + * dma_unmap_host_va - DMA unmapping of the given host virtual address. + * @hdev: habanalabs device structure + * @userptr: userptr to free + * + * This function does the following: + * - Unpins the physical pages + * - Frees the userptr structure + */ +static void dma_unmap_host_va(struct hl_device *hdev, + struct hl_userptr *userptr) +{ + hl_unpin_host_memory(hdev, userptr); + kfree(userptr); +} + +/* + * dram_pg_pool_do_release - free DRAM pages pool + * + * @ref : pointer to reference object + * + * This function does the following: + * - Frees the idr structure of physical pages handles + * - Frees the generic pool of DRAM physical pages + */ +static void dram_pg_pool_do_release(struct kref *ref) +{ + struct hl_vm *vm = container_of(ref, struct hl_vm, + dram_pg_pool_refcount); + + /* + * free the idr here as only here we know for sure that there are no + * allocated physical pages and hence there are no handles in use + */ + idr_destroy(&vm->phys_pg_pack_handles); + gen_pool_destroy(vm->dram_pg_pool); +} + +/* + * free_phys_pg_pack - free physical page pack + * @hdev: habanalabs device structure + * @phys_pg_pack: physical page pack to free + * + * This function does the following: + * - For DRAM memory only, iterate over the pack and free each physical block + * structure by returning it to the general pool + * - Free the hl_vm_phys_pg_pack structure + */ +static void free_phys_pg_pack(struct hl_device *hdev, + struct hl_vm_phys_pg_pack *phys_pg_pack) +{ + struct hl_vm *vm = &hdev->vm; + u64 i; + + if (!phys_pg_pack->created_from_userptr) { + if (phys_pg_pack->contiguous) { + gen_pool_free(vm->dram_pg_pool, phys_pg_pack->pages[0], + phys_pg_pack->total_size); + + for (i = 0; i < phys_pg_pack->npages ; i++) + kref_put(&vm->dram_pg_pool_refcount, + dram_pg_pool_do_release); + } else { + for (i = 0 ; i < phys_pg_pack->npages ; i++) { + gen_pool_free(vm->dram_pg_pool, + phys_pg_pack->pages[i], + phys_pg_pack->page_size); + kref_put(&vm->dram_pg_pool_refcount, + dram_pg_pool_do_release); + } + } + } + + kvfree(phys_pg_pack->pages); + kfree(phys_pg_pack); +} + +/* + * free_device_memory - free device memory + * + * @ctx : current context + * @handle : handle of the memory chunk to free + * + * This function does the following: + * - Free the device memory related to the given handle + */ +static int free_device_memory(struct hl_ctx *ctx, u32 handle) +{ + struct hl_device *hdev = ctx->hdev; + struct hl_vm *vm = &hdev->vm; + struct hl_vm_phys_pg_pack *phys_pg_pack; + + spin_lock(&vm->idr_lock); + phys_pg_pack = idr_find(&vm->phys_pg_pack_handles, handle); + if (phys_pg_pack) { + if (atomic_read(&phys_pg_pack->mapping_cnt) > 0) { + dev_err(hdev->dev, "handle %u is mapped, cannot free\n", + handle); + spin_unlock(&vm->idr_lock); + return -EINVAL; + } + + /* + * must remove from idr before the freeing of the physical + * pages as the refcount of the pool is also the trigger of the + * idr destroy + */ + idr_remove(&vm->phys_pg_pack_handles, handle); + spin_unlock(&vm->idr_lock); + + atomic64_sub(phys_pg_pack->total_size, &ctx->dram_phys_mem); + atomic64_sub(phys_pg_pack->total_size, &hdev->dram_used_mem); + + free_phys_pg_pack(hdev, phys_pg_pack); + } else { + spin_unlock(&vm->idr_lock); + dev_err(hdev->dev, + "free device memory failed, no match for handle %u\n", + handle); + return -EINVAL; + } + + return 0; +} + +/* + * clear_va_list_locked - free virtual addresses list + * + * @hdev : habanalabs device structure + * @va_list : list of virtual addresses to free + * + * This function does the following: + * - Iterate over the list and free each virtual addresses block + * + * This function should be called only when va_list lock is taken + */ +static void clear_va_list_locked(struct hl_device *hdev, + struct list_head *va_list) +{ + struct hl_vm_va_block *va_block, *tmp; + + list_for_each_entry_safe(va_block, tmp, va_list, node) { + list_del(&va_block->node); + kfree(va_block); + } +} + +/* + * print_va_list_locked - print virtual addresses list + * + * @hdev : habanalabs device structure + * @va_list : list of virtual addresses to print + * + * This function does the following: + * - Iterate over the list and print each virtual addresses block + * + * This function should be called only when va_list lock is taken + */ +static void print_va_list_locked(struct hl_device *hdev, + struct list_head *va_list) +{ +#if HL_MMU_DEBUG + struct hl_vm_va_block *va_block; + + dev_dbg(hdev->dev, "print va list:\n"); + + list_for_each_entry(va_block, va_list, node) + dev_dbg(hdev->dev, + "va block, start: 0x%llx, end: 0x%llx, size: %llu\n", + va_block->start, va_block->end, va_block->size); +#endif +} + +/* + * merge_va_blocks_locked - merge a virtual block if possible + * + * @hdev : pointer to the habanalabs device structure + * @va_list : pointer to the virtual addresses block list + * @va_block : virtual block to merge with adjacent blocks + * + * This function does the following: + * - Merge the given blocks with the adjacent blocks if their virtual ranges + * create a contiguous virtual range + * + * This Function should be called only when va_list lock is taken + */ +static void merge_va_blocks_locked(struct hl_device *hdev, + struct list_head *va_list, struct hl_vm_va_block *va_block) +{ + struct hl_vm_va_block *prev, *next; + + prev = list_prev_entry(va_block, node); + if (&prev->node != va_list && prev->end + 1 == va_block->start) { + prev->end = va_block->end; + prev->size = prev->end - prev->start; + list_del(&va_block->node); + kfree(va_block); + va_block = prev; + } + + next = list_next_entry(va_block, node); + if (&next->node != va_list && va_block->end + 1 == next->start) { + next->start = va_block->start; + next->size = next->end - next->start; + list_del(&va_block->node); + kfree(va_block); + } +} + +/* + * add_va_block_locked - add a virtual block to the virtual addresses list + * + * @hdev : pointer to the habanalabs device structure + * @va_list : pointer to the virtual addresses block list + * @start : start virtual address + * @end : end virtual address + * + * This function does the following: + * - Add the given block to the virtual blocks list and merge with other + * blocks if a contiguous virtual block can be created + * + * This Function should be called only when va_list lock is taken + */ +static int add_va_block_locked(struct hl_device *hdev, + struct list_head *va_list, u64 start, u64 end) +{ + struct hl_vm_va_block *va_block, *res = NULL; + u64 size = end - start; + + print_va_list_locked(hdev, va_list); + + list_for_each_entry(va_block, va_list, node) { + /* TODO: remove upon matureness */ + if (hl_mem_area_crosses_range(start, size, va_block->start, + va_block->end)) { + dev_err(hdev->dev, + "block crossing ranges at start 0x%llx, end 0x%llx\n", + va_block->start, va_block->end); + return -EINVAL; + } + + if (va_block->end < start) + res = va_block; + } + + va_block = kmalloc(sizeof(*va_block), GFP_KERNEL); + if (!va_block) + return -ENOMEM; + + va_block->start = start; + va_block->end = end; + va_block->size = size; + + if (!res) + list_add(&va_block->node, va_list); + else + list_add(&va_block->node, &res->node); + + merge_va_blocks_locked(hdev, va_list, va_block); + + print_va_list_locked(hdev, va_list); + + return 0; +} + +/* + * add_va_block - wrapper for add_va_block_locked + * + * @hdev : pointer to the habanalabs device structure + * @va_list : pointer to the virtual addresses block list + * @start : start virtual address + * @end : end virtual address + * + * This function does the following: + * - Takes the list lock and calls add_va_block_locked + */ +static inline int add_va_block(struct hl_device *hdev, + struct hl_va_range *va_range, u64 start, u64 end) +{ + int rc; + + mutex_lock(&va_range->lock); + rc = add_va_block_locked(hdev, &va_range->list, start, end); + mutex_unlock(&va_range->lock); + + return rc; +} + +/* + * get_va_block - get a virtual block with the requested size + * + * @hdev : pointer to the habanalabs device structure + * @va_range : pointer to the virtual addresses range + * @size : requested block size + * @hint_addr : hint for request address by the user + * @is_userptr : is host or DRAM memory + * + * This function does the following: + * - Iterate on the virtual block list to find a suitable virtual block for the + * requested size + * - Reserve the requested block and update the list + * - Return the start address of the virtual block + */ +static u64 get_va_block(struct hl_device *hdev, + struct hl_va_range *va_range, u64 size, u64 hint_addr, + bool is_userptr) +{ + struct hl_vm_va_block *va_block, *new_va_block = NULL; + u64 valid_start, valid_size, prev_start, prev_end, page_mask, + res_valid_start = 0, res_valid_size = 0; + u32 page_size; + bool add_prev = false; + + if (is_userptr) + /* + * We cannot know if the user allocated memory with huge pages + * or not, hence we continue with the biggest possible + * granularity. + */ + page_size = hdev->asic_prop.pmmu_huge.page_size; + else + page_size = hdev->asic_prop.dmmu.page_size; + + page_mask = ~((u64)page_size - 1); + + mutex_lock(&va_range->lock); + + print_va_list_locked(hdev, &va_range->list); + + list_for_each_entry(va_block, &va_range->list, node) { + /* calc the first possible aligned addr */ + valid_start = va_block->start; + + if (valid_start & (page_size - 1)) { + valid_start &= page_mask; + valid_start += page_size; + if (valid_start > va_block->end) + continue; + } + + valid_size = va_block->end - valid_start; + + if (valid_size >= size && + (!new_va_block || valid_size < res_valid_size)) { + new_va_block = va_block; + res_valid_start = valid_start; + res_valid_size = valid_size; + } + + if (hint_addr && hint_addr >= valid_start && + ((hint_addr + size) <= va_block->end)) { + new_va_block = va_block; + res_valid_start = hint_addr; + res_valid_size = valid_size; + break; + } + } + + if (!new_va_block) { + dev_err(hdev->dev, "no available va block for size %llu\n", + size); + goto out; + } + + if (res_valid_start > new_va_block->start) { + prev_start = new_va_block->start; + prev_end = res_valid_start - 1; + + new_va_block->start = res_valid_start; + new_va_block->size = res_valid_size; + + add_prev = true; + } + + if (new_va_block->size > size) { + new_va_block->start += size; + new_va_block->size = new_va_block->end - new_va_block->start; + } else { + list_del(&new_va_block->node); + kfree(new_va_block); + } + + if (add_prev) + add_va_block_locked(hdev, &va_range->list, prev_start, + prev_end); + + print_va_list_locked(hdev, &va_range->list); +out: + mutex_unlock(&va_range->lock); + + return res_valid_start; +} + +/* + * get_sg_info - get number of pages and the DMA address from SG list + * + * @sg : the SG list + * @dma_addr : pointer to DMA address to return + * + * Calculate the number of consecutive pages described by the SG list. Take the + * offset of the address in the first page, add to it the length and round it up + * to the number of needed pages. + */ +static u32 get_sg_info(struct scatterlist *sg, dma_addr_t *dma_addr) +{ + *dma_addr = sg_dma_address(sg); + + return ((((*dma_addr) & (PAGE_SIZE - 1)) + sg_dma_len(sg)) + + (PAGE_SIZE - 1)) >> PAGE_SHIFT; +} + +/* + * init_phys_pg_pack_from_userptr - initialize physical page pack from host + * memory + * @ctx: current context + * @userptr: userptr to initialize from + * @pphys_pg_pack: result pointer + * + * This function does the following: + * - Pin the physical pages related to the given virtual block + * - Create a physical page pack from the physical pages related to the given + * virtual block + */ +static int init_phys_pg_pack_from_userptr(struct hl_ctx *ctx, + struct hl_userptr *userptr, + struct hl_vm_phys_pg_pack **pphys_pg_pack) +{ + struct hl_vm_phys_pg_pack *phys_pg_pack; + struct scatterlist *sg; + dma_addr_t dma_addr; + u64 page_mask, total_npages; + u32 npages, page_size = PAGE_SIZE, + huge_page_size = ctx->hdev->asic_prop.pmmu_huge.page_size; + bool first = true, is_huge_page_opt = true; + int rc, i, j; + u32 pgs_in_huge_page = huge_page_size >> __ffs(page_size); + + phys_pg_pack = kzalloc(sizeof(*phys_pg_pack), GFP_KERNEL); + if (!phys_pg_pack) + return -ENOMEM; + + phys_pg_pack->vm_type = userptr->vm_type; + phys_pg_pack->created_from_userptr = true; + phys_pg_pack->asid = ctx->asid; + atomic_set(&phys_pg_pack->mapping_cnt, 1); + + /* Only if all dma_addrs are aligned to 2MB and their + * sizes is at least 2MB, we can use huge page mapping. + * We limit the 2MB optimization to this condition, + * since later on we acquire the related VA range as one + * consecutive block. + */ + total_npages = 0; + for_each_sg(userptr->sgt->sgl, sg, userptr->sgt->nents, i) { + npages = get_sg_info(sg, &dma_addr); + + total_npages += npages; + + if ((npages % pgs_in_huge_page) || + (dma_addr & (huge_page_size - 1))) + is_huge_page_opt = false; + } + + if (is_huge_page_opt) { + page_size = huge_page_size; + do_div(total_npages, pgs_in_huge_page); + } + + page_mask = ~(((u64) page_size) - 1); + + phys_pg_pack->pages = kvmalloc_array(total_npages, sizeof(u64), + GFP_KERNEL); + if (!phys_pg_pack->pages) { + rc = -ENOMEM; + goto page_pack_arr_mem_err; + } + + phys_pg_pack->npages = total_npages; + phys_pg_pack->page_size = page_size; + phys_pg_pack->total_size = total_npages * page_size; + + j = 0; + for_each_sg(userptr->sgt->sgl, sg, userptr->sgt->nents, i) { + npages = get_sg_info(sg, &dma_addr); + + /* align down to physical page size and save the offset */ + if (first) { + first = false; + phys_pg_pack->offset = dma_addr & (page_size - 1); + dma_addr &= page_mask; + } + + while (npages) { + phys_pg_pack->pages[j++] = dma_addr; + dma_addr += page_size; + + if (is_huge_page_opt) + npages -= pgs_in_huge_page; + else + npages--; + } + } + + *pphys_pg_pack = phys_pg_pack; + + return 0; + +page_pack_arr_mem_err: + kfree(phys_pg_pack); + + return rc; +} + +/* + * map_phys_pg_pack - maps the physical page pack. + * @ctx: current context + * @vaddr: start address of the virtual area to map from + * @phys_pg_pack: the pack of physical pages to map to + * + * This function does the following: + * - Maps each chunk of virtual memory to matching physical chunk + * - Stores number of successful mappings in the given argument + * - Returns 0 on success, error code otherwise + */ +static int map_phys_pg_pack(struct hl_ctx *ctx, u64 vaddr, + struct hl_vm_phys_pg_pack *phys_pg_pack) +{ + struct hl_device *hdev = ctx->hdev; + u64 next_vaddr = vaddr, paddr, mapped_pg_cnt = 0, i; + u32 page_size = phys_pg_pack->page_size; + int rc = 0; + + for (i = 0 ; i < phys_pg_pack->npages ; i++) { + paddr = phys_pg_pack->pages[i]; + + rc = hl_mmu_map(ctx, next_vaddr, paddr, page_size, + (i + 1) == phys_pg_pack->npages); + if (rc) { + dev_err(hdev->dev, + "map failed for handle %u, npages: %llu, mapped: %llu", + phys_pg_pack->handle, phys_pg_pack->npages, + mapped_pg_cnt); + goto err; + } + + mapped_pg_cnt++; + next_vaddr += page_size; + } + + return 0; + +err: + next_vaddr = vaddr; + for (i = 0 ; i < mapped_pg_cnt ; i++) { + if (hl_mmu_unmap(ctx, next_vaddr, page_size, + (i + 1) == mapped_pg_cnt)) + dev_warn_ratelimited(hdev->dev, + "failed to unmap handle %u, va: 0x%llx, pa: 0x%llx, page size: %u\n", + phys_pg_pack->handle, next_vaddr, + phys_pg_pack->pages[i], page_size); + + next_vaddr += page_size; + } + + return rc; +} + +/* + * unmap_phys_pg_pack - unmaps the physical page pack + * @ctx: current context + * @vaddr: start address of the virtual area to unmap + * @phys_pg_pack: the pack of physical pages to unmap + */ +static void unmap_phys_pg_pack(struct hl_ctx *ctx, u64 vaddr, + struct hl_vm_phys_pg_pack *phys_pg_pack) +{ + struct hl_device *hdev = ctx->hdev; + u64 next_vaddr, i; + u32 page_size; + + page_size = phys_pg_pack->page_size; + next_vaddr = vaddr; + + for (i = 0 ; i < phys_pg_pack->npages ; i++, next_vaddr += page_size) { + if (hl_mmu_unmap(ctx, next_vaddr, page_size, + (i + 1) == phys_pg_pack->npages)) + dev_warn_ratelimited(hdev->dev, + "unmap failed for vaddr: 0x%llx\n", next_vaddr); + + /* + * unmapping on Palladium can be really long, so avoid a CPU + * soft lockup bug by sleeping a little between unmapping pages + */ + if (hdev->pldm) + usleep_range(500, 1000); + } +} + +static int get_paddr_from_handle(struct hl_ctx *ctx, struct hl_mem_in *args, + u64 *paddr) +{ + struct hl_device *hdev = ctx->hdev; + struct hl_vm *vm = &hdev->vm; + struct hl_vm_phys_pg_pack *phys_pg_pack; + u32 handle; + + handle = lower_32_bits(args->map_device.handle); + spin_lock(&vm->idr_lock); + phys_pg_pack = idr_find(&vm->phys_pg_pack_handles, handle); + if (!phys_pg_pack) { + spin_unlock(&vm->idr_lock); + dev_err(hdev->dev, "no match for handle %u\n", handle); + return -EINVAL; + } + + *paddr = phys_pg_pack->pages[0]; + + spin_unlock(&vm->idr_lock); + + return 0; +} + +/* + * map_device_va - map the given memory + * + * @ctx : current context + * @args : host parameters with handle/host virtual address + * @device_addr : pointer to result device virtual address + * + * This function does the following: + * - If given a physical device memory handle, map to a device virtual block + * and return the start address of this block + * - If given a host virtual address and size, find the related physical pages, + * map a device virtual block to this pages and return the start address of + * this block + */ +static int map_device_va(struct hl_ctx *ctx, struct hl_mem_in *args, + u64 *device_addr) +{ + struct hl_device *hdev = ctx->hdev; + struct hl_vm *vm = &hdev->vm; + struct hl_vm_phys_pg_pack *phys_pg_pack; + struct hl_userptr *userptr = NULL; + struct hl_vm_hash_node *hnode; + struct hl_va_range *va_range; + enum vm_type_t *vm_type; + u64 ret_vaddr, hint_addr; + u32 handle = 0; + int rc; + bool is_userptr = args->flags & HL_MEM_USERPTR; + + /* Assume failure */ + *device_addr = 0; + + if (is_userptr) { + u64 addr = args->map_host.host_virt_addr, + size = args->map_host.mem_size; + + rc = dma_map_host_va(hdev, addr, size, &userptr); + if (rc) { + dev_err(hdev->dev, "failed to get userptr from va\n"); + return rc; + } + + rc = init_phys_pg_pack_from_userptr(ctx, userptr, + &phys_pg_pack); + if (rc) { + dev_err(hdev->dev, + "unable to init page pack for vaddr 0x%llx\n", + addr); + goto init_page_pack_err; + } + + vm_type = (enum vm_type_t *) userptr; + hint_addr = args->map_host.hint_addr; + handle = phys_pg_pack->handle; + } else { + handle = lower_32_bits(args->map_device.handle); + + spin_lock(&vm->idr_lock); + phys_pg_pack = idr_find(&vm->phys_pg_pack_handles, handle); + if (!phys_pg_pack) { + spin_unlock(&vm->idr_lock); + dev_err(hdev->dev, + "no match for handle %u\n", handle); + return -EINVAL; + } + + /* increment now to avoid freeing device memory while mapping */ + atomic_inc(&phys_pg_pack->mapping_cnt); + + spin_unlock(&vm->idr_lock); + + vm_type = (enum vm_type_t *) phys_pg_pack; + + hint_addr = args->map_device.hint_addr; + } + + /* + * relevant for mapping device physical memory only, as host memory is + * implicitly shared + */ + if (!is_userptr && !(phys_pg_pack->flags & HL_MEM_SHARED) && + phys_pg_pack->asid != ctx->asid) { + dev_err(hdev->dev, + "Failed to map memory, handle %u is not shared\n", + handle); + rc = -EPERM; + goto shared_err; + } + + hnode = kzalloc(sizeof(*hnode), GFP_KERNEL); + if (!hnode) { + rc = -ENOMEM; + goto hnode_err; + } + + if (is_userptr) + if (phys_pg_pack->page_size == hdev->asic_prop.pmmu.page_size) + va_range = ctx->host_va_range; + else + va_range = ctx->host_huge_va_range; + else + va_range = ctx->dram_va_range; + + ret_vaddr = get_va_block(hdev, va_range, phys_pg_pack->total_size, + hint_addr, is_userptr); + if (!ret_vaddr) { + dev_err(hdev->dev, "no available va block for handle %u\n", + handle); + rc = -ENOMEM; + goto va_block_err; + } + + mutex_lock(&ctx->mmu_lock); + + rc = map_phys_pg_pack(ctx, ret_vaddr, phys_pg_pack); + if (rc) { + mutex_unlock(&ctx->mmu_lock); + dev_err(hdev->dev, "mapping page pack failed for handle %u\n", + handle); + goto map_err; + } + + rc = hdev->asic_funcs->mmu_invalidate_cache(hdev, false, *vm_type); + + mutex_unlock(&ctx->mmu_lock); + + if (rc) { + dev_err(hdev->dev, + "mapping handle %u failed due to MMU cache invalidation\n", + handle); + goto map_err; + } + + ret_vaddr += phys_pg_pack->offset; + + hnode->ptr = vm_type; + hnode->vaddr = ret_vaddr; + + mutex_lock(&ctx->mem_hash_lock); + hash_add(ctx->mem_hash, &hnode->node, ret_vaddr); + mutex_unlock(&ctx->mem_hash_lock); + + *device_addr = ret_vaddr; + + if (is_userptr) + free_phys_pg_pack(hdev, phys_pg_pack); + + return 0; + +map_err: + if (add_va_block(hdev, va_range, ret_vaddr, + ret_vaddr + phys_pg_pack->total_size - 1)) + dev_warn(hdev->dev, + "release va block failed for handle 0x%x, vaddr: 0x%llx\n", + handle, ret_vaddr); + +va_block_err: + kfree(hnode); +hnode_err: +shared_err: + atomic_dec(&phys_pg_pack->mapping_cnt); + if (is_userptr) + free_phys_pg_pack(hdev, phys_pg_pack); +init_page_pack_err: + if (is_userptr) + dma_unmap_host_va(hdev, userptr); + + return rc; +} + +/* + * unmap_device_va - unmap the given device virtual address + * + * @ctx : current context + * @vaddr : device virtual address to unmap + * @ctx_free : true if in context free flow, false otherwise. + * + * This function does the following: + * - Unmap the physical pages related to the given virtual address + * - return the device virtual block to the virtual block list + */ +static int unmap_device_va(struct hl_ctx *ctx, u64 vaddr, bool ctx_free) +{ + struct hl_device *hdev = ctx->hdev; + struct hl_vm_phys_pg_pack *phys_pg_pack = NULL; + struct hl_vm_hash_node *hnode = NULL; + struct hl_userptr *userptr = NULL; + struct hl_va_range *va_range; + enum vm_type_t *vm_type; + bool is_userptr; + int rc = 0; + + /* protect from double entrance */ + mutex_lock(&ctx->mem_hash_lock); + hash_for_each_possible(ctx->mem_hash, hnode, node, (unsigned long)vaddr) + if (vaddr == hnode->vaddr) + break; + + if (!hnode) { + mutex_unlock(&ctx->mem_hash_lock); + dev_err(hdev->dev, + "unmap failed, no mem hnode for vaddr 0x%llx\n", + vaddr); + return -EINVAL; + } + + hash_del(&hnode->node); + mutex_unlock(&ctx->mem_hash_lock); + + vm_type = hnode->ptr; + + if (*vm_type == VM_TYPE_USERPTR) { + is_userptr = true; + userptr = hnode->ptr; + rc = init_phys_pg_pack_from_userptr(ctx, userptr, + &phys_pg_pack); + if (rc) { + dev_err(hdev->dev, + "unable to init page pack for vaddr 0x%llx\n", + vaddr); + goto vm_type_err; + } + + if (phys_pg_pack->page_size == + hdev->asic_prop.pmmu.page_size) + va_range = ctx->host_va_range; + else + va_range = ctx->host_huge_va_range; + } else if (*vm_type == VM_TYPE_PHYS_PACK) { + is_userptr = false; + va_range = ctx->dram_va_range; + phys_pg_pack = hnode->ptr; + } else { + dev_warn(hdev->dev, + "unmap failed, unknown vm desc for vaddr 0x%llx\n", + vaddr); + rc = -EFAULT; + goto vm_type_err; + } + + if (atomic_read(&phys_pg_pack->mapping_cnt) == 0) { + dev_err(hdev->dev, "vaddr 0x%llx is not mapped\n", vaddr); + rc = -EINVAL; + goto mapping_cnt_err; + } + + vaddr &= ~(((u64) phys_pg_pack->page_size) - 1); + + mutex_lock(&ctx->mmu_lock); + + unmap_phys_pg_pack(ctx, vaddr, phys_pg_pack); + + /* + * During context free this function is called in a loop to clean all + * the context mappings. Hence the cache invalidation can be called once + * at the loop end rather than for each iteration + */ + if (!ctx_free) + rc = hdev->asic_funcs->mmu_invalidate_cache(hdev, true, + *vm_type); + + mutex_unlock(&ctx->mmu_lock); + + /* + * If the context is closing we don't need to check for the MMU cache + * invalidation return code and update the VA free list as in this flow + * we invalidate the MMU cache outside of this unmap function and the VA + * free list will be freed anyway. + */ + if (!ctx_free) { + int tmp_rc; + + if (rc) + dev_err(hdev->dev, + "unmapping vaddr 0x%llx failed due to MMU cache invalidation\n", + vaddr); + + tmp_rc = add_va_block(hdev, va_range, vaddr, + vaddr + phys_pg_pack->total_size - 1); + if (tmp_rc) { + dev_warn(hdev->dev, + "add va block failed for vaddr: 0x%llx\n", + vaddr); + if (!rc) + rc = tmp_rc; + } + } + + atomic_dec(&phys_pg_pack->mapping_cnt); + kfree(hnode); + + if (is_userptr) { + free_phys_pg_pack(hdev, phys_pg_pack); + dma_unmap_host_va(hdev, userptr); + } + + return rc; + +mapping_cnt_err: + if (is_userptr) + free_phys_pg_pack(hdev, phys_pg_pack); +vm_type_err: + mutex_lock(&ctx->mem_hash_lock); + hash_add(ctx->mem_hash, &hnode->node, vaddr); + mutex_unlock(&ctx->mem_hash_lock); + + return rc; +} + +static int mem_ioctl_no_mmu(struct hl_fpriv *hpriv, union hl_mem_args *args) +{ + struct hl_device *hdev = hpriv->hdev; + struct hl_ctx *ctx = hpriv->ctx; + u64 device_addr = 0; + u32 handle = 0; + int rc; + + switch (args->in.op) { + case HL_MEM_OP_ALLOC: + if (args->in.alloc.mem_size == 0) { + dev_err(hdev->dev, + "alloc size must be larger than 0\n"); + rc = -EINVAL; + goto out; + } + + /* Force contiguous as there are no real MMU + * translations to overcome physical memory gaps + */ + args->in.flags |= HL_MEM_CONTIGUOUS; + rc = alloc_device_memory(ctx, &args->in, &handle); + + memset(args, 0, sizeof(*args)); + args->out.handle = (__u64) handle; + break; + + case HL_MEM_OP_FREE: + rc = free_device_memory(ctx, args->in.free.handle); + break; + + case HL_MEM_OP_MAP: + if (args->in.flags & HL_MEM_USERPTR) { + device_addr = args->in.map_host.host_virt_addr; + rc = 0; + } else { + rc = get_paddr_from_handle(ctx, &args->in, + &device_addr); + } + + memset(args, 0, sizeof(*args)); + args->out.device_virt_addr = device_addr; + break; + + case HL_MEM_OP_UNMAP: + rc = 0; + break; + + default: + dev_err(hdev->dev, "Unknown opcode for memory IOCTL\n"); + rc = -ENOTTY; + break; + } + +out: + return rc; +} + +int hl_mem_ioctl(struct hl_fpriv *hpriv, void *data) +{ + union hl_mem_args *args = data; + struct hl_device *hdev = hpriv->hdev; + struct hl_ctx *ctx = hpriv->ctx; + u64 device_addr = 0; + u32 handle = 0; + int rc; + + if (hl_device_disabled_or_in_reset(hdev)) { + dev_warn_ratelimited(hdev->dev, + "Device is %s. Can't execute MEMORY IOCTL\n", + atomic_read(&hdev->in_reset) ? "in_reset" : "disabled"); + return -EBUSY; + } + + if (!hdev->mmu_enable) + return mem_ioctl_no_mmu(hpriv, args); + + switch (args->in.op) { + case HL_MEM_OP_ALLOC: + if (!hdev->dram_supports_virtual_memory) { + dev_err(hdev->dev, "DRAM alloc is not supported\n"); + rc = -EINVAL; + goto out; + } + + if (args->in.alloc.mem_size == 0) { + dev_err(hdev->dev, + "alloc size must be larger than 0\n"); + rc = -EINVAL; + goto out; + } + rc = alloc_device_memory(ctx, &args->in, &handle); + + memset(args, 0, sizeof(*args)); + args->out.handle = (__u64) handle; + break; + + case HL_MEM_OP_FREE: + rc = free_device_memory(ctx, args->in.free.handle); + break; + + case HL_MEM_OP_MAP: + rc = map_device_va(ctx, &args->in, &device_addr); + + memset(args, 0, sizeof(*args)); + args->out.device_virt_addr = device_addr; + break; + + case HL_MEM_OP_UNMAP: + rc = unmap_device_va(ctx, args->in.unmap.device_virt_addr, + false); + break; + + default: + dev_err(hdev->dev, "Unknown opcode for memory IOCTL\n"); + rc = -ENOTTY; + break; + } + +out: + return rc; +} + +static int get_user_memory(struct hl_device *hdev, u64 addr, u64 size, + u32 npages, u64 start, u32 offset, + struct hl_userptr *userptr) +{ + int rc; + + if (!access_ok((void __user *) (uintptr_t) addr, size)) { + dev_err(hdev->dev, "user pointer is invalid - 0x%llx\n", addr); + return -EFAULT; + } + + userptr->vec = frame_vector_create(npages); + if (!userptr->vec) { + dev_err(hdev->dev, "Failed to create frame vector\n"); + return -ENOMEM; + } + + rc = get_vaddr_frames(start, npages, FOLL_FORCE | FOLL_WRITE, + userptr->vec); + + if (rc != npages) { + dev_err(hdev->dev, + "Failed to map host memory, user ptr probably wrong\n"); + if (rc < 0) + goto destroy_framevec; + rc = -EFAULT; + goto put_framevec; + } + + if (frame_vector_to_pages(userptr->vec) < 0) { + dev_err(hdev->dev, + "Failed to translate frame vector to pages\n"); + rc = -EFAULT; + goto put_framevec; + } + + rc = sg_alloc_table_from_pages(userptr->sgt, + frame_vector_pages(userptr->vec), + npages, offset, size, GFP_ATOMIC); + if (rc < 0) { + dev_err(hdev->dev, "failed to create SG table from pages\n"); + goto put_framevec; + } + + return 0; + +put_framevec: + put_vaddr_frames(userptr->vec); +destroy_framevec: + frame_vector_destroy(userptr->vec); + return rc; +} + +/* + * hl_pin_host_memory - pins a chunk of host memory. + * @hdev: pointer to the habanalabs device structure + * @addr: the host virtual address of the memory area + * @size: the size of the memory area + * @userptr: pointer to hl_userptr structure + * + * This function does the following: + * - Pins the physical pages + * - Create an SG list from those pages + */ +int hl_pin_host_memory(struct hl_device *hdev, u64 addr, u64 size, + struct hl_userptr *userptr) +{ + u64 start, end; + u32 npages, offset; + int rc; + + if (!size) { + dev_err(hdev->dev, "size to pin is invalid - %llu\n", size); + return -EINVAL; + } + + /* + * If the combination of the address and size requested for this memory + * region causes an integer overflow, return error. + */ + if (((addr + size) < addr) || + PAGE_ALIGN(addr + size) < (addr + size)) { + dev_err(hdev->dev, + "user pointer 0x%llx + %llu causes integer overflow\n", + addr, size); + return -EINVAL; + } + + /* + * This function can be called also from data path, hence use atomic + * always as it is not a big allocation. + */ + userptr->sgt = kzalloc(sizeof(*userptr->sgt), GFP_ATOMIC); + if (!userptr->sgt) + return -ENOMEM; + + start = addr & PAGE_MASK; + offset = addr & ~PAGE_MASK; + end = PAGE_ALIGN(addr + size); + npages = (end - start) >> PAGE_SHIFT; + + userptr->size = size; + userptr->addr = addr; + userptr->dma_mapped = false; + INIT_LIST_HEAD(&userptr->job_node); + + rc = get_user_memory(hdev, addr, size, npages, start, offset, + userptr); + if (rc) { + dev_err(hdev->dev, + "failed to get user memory for address 0x%llx\n", + addr); + goto free_sgt; + } + + hl_debugfs_add_userptr(hdev, userptr); + + return 0; + +free_sgt: + kfree(userptr->sgt); + return rc; +} + +/* + * hl_unpin_host_memory - unpins a chunk of host memory. + * @hdev: pointer to the habanalabs device structure + * @userptr: pointer to hl_userptr structure + * + * This function does the following: + * - Unpins the physical pages related to the host memory + * - Free the SG list + */ +void hl_unpin_host_memory(struct hl_device *hdev, struct hl_userptr *userptr) +{ + struct page **pages; + + hl_debugfs_remove_userptr(hdev, userptr); + + if (userptr->dma_mapped) + hdev->asic_funcs->hl_dma_unmap_sg(hdev, userptr->sgt->sgl, + userptr->sgt->nents, + userptr->dir); + + pages = frame_vector_pages(userptr->vec); + if (!IS_ERR(pages)) { + int i; + + for (i = 0; i < frame_vector_count(userptr->vec); i++) + set_page_dirty_lock(pages[i]); + } + put_vaddr_frames(userptr->vec); + frame_vector_destroy(userptr->vec); + + list_del(&userptr->job_node); + + sg_free_table(userptr->sgt); + kfree(userptr->sgt); +} + +/* + * hl_userptr_delete_list - clear userptr list + * + * @hdev : pointer to the habanalabs device structure + * @userptr_list : pointer to the list to clear + * + * This function does the following: + * - Iterates over the list and unpins the host memory and frees the userptr + * structure. + */ +void hl_userptr_delete_list(struct hl_device *hdev, + struct list_head *userptr_list) +{ + struct hl_userptr *userptr, *tmp; + + list_for_each_entry_safe(userptr, tmp, userptr_list, job_node) { + hl_unpin_host_memory(hdev, userptr); + kfree(userptr); + } + + INIT_LIST_HEAD(userptr_list); +} + +/* + * hl_userptr_is_pinned - returns whether the given userptr is pinned + * + * @hdev : pointer to the habanalabs device structure + * @userptr_list : pointer to the list to clear + * @userptr : pointer to userptr to check + * + * This function does the following: + * - Iterates over the list and checks if the given userptr is in it, means is + * pinned. If so, returns true, otherwise returns false. + */ +bool hl_userptr_is_pinned(struct hl_device *hdev, u64 addr, + u32 size, struct list_head *userptr_list, + struct hl_userptr **userptr) +{ + list_for_each_entry((*userptr), userptr_list, job_node) { + if ((addr == (*userptr)->addr) && (size == (*userptr)->size)) + return true; + } + + return false; +} + +/* + * va_range_init - initialize virtual addresses range + * @hdev: pointer to the habanalabs device structure + * @va_range: pointer to the range to initialize + * @start: range start address + * @end: range end address + * + * This function does the following: + * - Initializes the virtual addresses list of the given range with the given + * addresses. + */ +static int va_range_init(struct hl_device *hdev, struct hl_va_range *va_range, + u64 start, u64 end) +{ + int rc; + + INIT_LIST_HEAD(&va_range->list); + + /* PAGE_SIZE alignment */ + + if (start & (PAGE_SIZE - 1)) { + start &= PAGE_MASK; + start += PAGE_SIZE; + } + + if (end & (PAGE_SIZE - 1)) + end &= PAGE_MASK; + + if (start >= end) { + dev_err(hdev->dev, "too small vm range for va list\n"); + return -EFAULT; + } + + rc = add_va_block(hdev, va_range, start, end); + + if (rc) { + dev_err(hdev->dev, "Failed to init host va list\n"); + return rc; + } + + va_range->start_addr = start; + va_range->end_addr = end; + + return 0; +} + +/* + * va_range_fini() - clear a virtual addresses range + * @hdev: pointer to the habanalabs structure + * va_range: pointer to virtual addresses range + * + * This function does the following: + * - Frees the virtual addresses block list and its lock + */ +static void va_range_fini(struct hl_device *hdev, + struct hl_va_range *va_range) +{ + mutex_lock(&va_range->lock); + clear_va_list_locked(hdev, &va_range->list); + mutex_unlock(&va_range->lock); + + mutex_destroy(&va_range->lock); + kfree(va_range); +} + +/* + * vm_ctx_init_with_ranges() - initialize virtual memory for context + * @ctx: pointer to the habanalabs context structure + * @host_range_start: host virtual addresses range start. + * @host_range_end: host virtual addresses range end. + * @host_huge_range_start: host virtual addresses range start for memory + * allocated with huge pages. + * @host_huge_range_end: host virtual addresses range end for memory allocated + * with huge pages. + * @dram_range_start: dram virtual addresses range start. + * @dram_range_end: dram virtual addresses range end. + * + * This function initializes the following: + * - MMU for context + * - Virtual address to area descriptor hashtable + * - Virtual block list of available virtual memory + */ +static int vm_ctx_init_with_ranges(struct hl_ctx *ctx, + u64 host_range_start, + u64 host_range_end, + u64 host_huge_range_start, + u64 host_huge_range_end, + u64 dram_range_start, + u64 dram_range_end) +{ + struct hl_device *hdev = ctx->hdev; + int rc; + + ctx->host_va_range = kzalloc(sizeof(*ctx->host_va_range), GFP_KERNEL); + if (!ctx->host_va_range) + return -ENOMEM; + + ctx->host_huge_va_range = kzalloc(sizeof(*ctx->host_huge_va_range), + GFP_KERNEL); + if (!ctx->host_huge_va_range) { + rc = -ENOMEM; + goto host_huge_va_range_err; + } + + ctx->dram_va_range = kzalloc(sizeof(*ctx->dram_va_range), GFP_KERNEL); + if (!ctx->dram_va_range) { + rc = -ENOMEM; + goto dram_va_range_err; + } + + rc = hl_mmu_ctx_init(ctx); + if (rc) { + dev_err(hdev->dev, "failed to init context %d\n", ctx->asid); + goto mmu_ctx_err; + } + + mutex_init(&ctx->mem_hash_lock); + hash_init(ctx->mem_hash); + + mutex_init(&ctx->host_va_range->lock); + + rc = va_range_init(hdev, ctx->host_va_range, host_range_start, + host_range_end); + if (rc) { + dev_err(hdev->dev, "failed to init host vm range\n"); + goto host_page_range_err; + } + + if (hdev->pmmu_huge_range) { + mutex_init(&ctx->host_huge_va_range->lock); + + rc = va_range_init(hdev, ctx->host_huge_va_range, + host_huge_range_start, + host_huge_range_end); + if (rc) { + dev_err(hdev->dev, + "failed to init host huge vm range\n"); + goto host_hpage_range_err; + } + } else { + ctx->host_huge_va_range = ctx->host_va_range; + } + + mutex_init(&ctx->dram_va_range->lock); + + rc = va_range_init(hdev, ctx->dram_va_range, dram_range_start, + dram_range_end); + if (rc) { + dev_err(hdev->dev, "failed to init dram vm range\n"); + goto dram_vm_err; + } + + hl_debugfs_add_ctx_mem_hash(hdev, ctx); + + return 0; + +dram_vm_err: + mutex_destroy(&ctx->dram_va_range->lock); + + if (hdev->pmmu_huge_range) { + mutex_lock(&ctx->host_huge_va_range->lock); + clear_va_list_locked(hdev, &ctx->host_huge_va_range->list); + mutex_unlock(&ctx->host_huge_va_range->lock); + } +host_hpage_range_err: + if (hdev->pmmu_huge_range) + mutex_destroy(&ctx->host_huge_va_range->lock); + mutex_lock(&ctx->host_va_range->lock); + clear_va_list_locked(hdev, &ctx->host_va_range->list); + mutex_unlock(&ctx->host_va_range->lock); +host_page_range_err: + mutex_destroy(&ctx->host_va_range->lock); + mutex_destroy(&ctx->mem_hash_lock); + hl_mmu_ctx_fini(ctx); +mmu_ctx_err: + kfree(ctx->dram_va_range); +dram_va_range_err: + kfree(ctx->host_huge_va_range); +host_huge_va_range_err: + kfree(ctx->host_va_range); + + return rc; +} + +int hl_vm_ctx_init(struct hl_ctx *ctx) +{ + struct asic_fixed_properties *prop = &ctx->hdev->asic_prop; + u64 host_range_start, host_range_end, host_huge_range_start, + host_huge_range_end, dram_range_start, dram_range_end; + + atomic64_set(&ctx->dram_phys_mem, 0); + + /* + * - If MMU is enabled, init the ranges as usual. + * - If MMU is disabled, in case of host mapping, the returned address + * is the given one. + * In case of DRAM mapping, the returned address is the physical + * address of the memory related to the given handle. + */ + if (ctx->hdev->mmu_enable) { + dram_range_start = prop->dmmu.start_addr; + dram_range_end = prop->dmmu.end_addr; + host_range_start = prop->pmmu.start_addr; + host_range_end = prop->pmmu.end_addr; + host_huge_range_start = prop->pmmu_huge.start_addr; + host_huge_range_end = prop->pmmu_huge.end_addr; + } else { + dram_range_start = prop->dram_user_base_address; + dram_range_end = prop->dram_end_address; + host_range_start = prop->dram_user_base_address; + host_range_end = prop->dram_end_address; + host_huge_range_start = prop->dram_user_base_address; + host_huge_range_end = prop->dram_end_address; + } + + return vm_ctx_init_with_ranges(ctx, host_range_start, host_range_end, + host_huge_range_start, + host_huge_range_end, + dram_range_start, + dram_range_end); +} + +/* + * hl_vm_ctx_fini - virtual memory teardown of context + * + * @ctx : pointer to the habanalabs context structure + * + * This function perform teardown the following: + * - Virtual block list of available virtual memory + * - Virtual address to area descriptor hashtable + * - MMU for context + * + * In addition this function does the following: + * - Unmaps the existing hashtable nodes if the hashtable is not empty. The + * hashtable should be empty as no valid mappings should exist at this + * point. + * - Frees any existing physical page list from the idr which relates to the + * current context asid. + * - This function checks the virtual block list for correctness. At this point + * the list should contain one element which describes the whole virtual + * memory range of the context. Otherwise, a warning is printed. + */ +void hl_vm_ctx_fini(struct hl_ctx *ctx) +{ + struct hl_device *hdev = ctx->hdev; + struct hl_vm *vm = &hdev->vm; + struct hl_vm_phys_pg_pack *phys_pg_list; + struct hl_vm_hash_node *hnode; + struct hlist_node *tmp_node; + int i; + + hl_debugfs_remove_ctx_mem_hash(hdev, ctx); + + /* + * Clearly something went wrong on hard reset so no point in printing + * another side effect error + */ + if (!hdev->hard_reset_pending && !hash_empty(ctx->mem_hash)) + dev_notice(hdev->dev, + "user released device without removing its memory mappings\n"); + + hash_for_each_safe(ctx->mem_hash, i, tmp_node, hnode, node) { + dev_dbg(hdev->dev, + "hl_mem_hash_node of vaddr 0x%llx of asid %d is still alive\n", + hnode->vaddr, ctx->asid); + unmap_device_va(ctx, hnode->vaddr, true); + } + + /* invalidate the cache once after the unmapping loop */ + hdev->asic_funcs->mmu_invalidate_cache(hdev, true, VM_TYPE_USERPTR); + hdev->asic_funcs->mmu_invalidate_cache(hdev, true, VM_TYPE_PHYS_PACK); + + spin_lock(&vm->idr_lock); + idr_for_each_entry(&vm->phys_pg_pack_handles, phys_pg_list, i) + if (phys_pg_list->asid == ctx->asid) { + dev_dbg(hdev->dev, + "page list 0x%px of asid %d is still alive\n", + phys_pg_list, ctx->asid); + atomic64_sub(phys_pg_list->total_size, + &hdev->dram_used_mem); + free_phys_pg_pack(hdev, phys_pg_list); + idr_remove(&vm->phys_pg_pack_handles, i); + } + spin_unlock(&vm->idr_lock); + + va_range_fini(hdev, ctx->dram_va_range); + if (hdev->pmmu_huge_range) + va_range_fini(hdev, ctx->host_huge_va_range); + va_range_fini(hdev, ctx->host_va_range); + + mutex_destroy(&ctx->mem_hash_lock); + hl_mmu_ctx_fini(ctx); +} + +/* + * hl_vm_init - initialize virtual memory module + * + * @hdev : pointer to the habanalabs device structure + * + * This function initializes the following: + * - MMU module + * - DRAM physical pages pool of 2MB + * - Idr for device memory allocation handles + */ +int hl_vm_init(struct hl_device *hdev) +{ + struct asic_fixed_properties *prop = &hdev->asic_prop; + struct hl_vm *vm = &hdev->vm; + int rc; + + vm->dram_pg_pool = gen_pool_create(__ffs(prop->dram_page_size), -1); + if (!vm->dram_pg_pool) { + dev_err(hdev->dev, "Failed to create dram page pool\n"); + return -ENOMEM; + } + + kref_init(&vm->dram_pg_pool_refcount); + + rc = gen_pool_add(vm->dram_pg_pool, prop->dram_user_base_address, + prop->dram_end_address - prop->dram_user_base_address, + -1); + + if (rc) { + dev_err(hdev->dev, + "Failed to add memory to dram page pool %d\n", rc); + goto pool_add_err; + } + + spin_lock_init(&vm->idr_lock); + idr_init(&vm->phys_pg_pack_handles); + + atomic64_set(&hdev->dram_used_mem, 0); + + vm->init_done = true; + + return 0; + +pool_add_err: + gen_pool_destroy(vm->dram_pg_pool); + + return rc; +} + +/* + * hl_vm_fini - virtual memory module teardown + * + * @hdev : pointer to the habanalabs device structure + * + * This function perform teardown to the following: + * - Idr for device memory allocation handles + * - DRAM physical pages pool of 2MB + * - MMU module + */ +void hl_vm_fini(struct hl_device *hdev) +{ + struct hl_vm *vm = &hdev->vm; + + if (!vm->init_done) + return; + + /* + * At this point all the contexts should be freed and hence no DRAM + * memory should be in use. Hence the DRAM pool should be freed here. + */ + if (kref_put(&vm->dram_pg_pool_refcount, dram_pg_pool_do_release) != 1) + dev_warn(hdev->dev, "dram_pg_pool was not destroyed on %s\n", + __func__); + + vm->init_done = false; +} diff --git a/drivers/misc/habanalabs/common/mmu.c b/drivers/misc/habanalabs/common/mmu.c new file mode 100644 index 000000000000..04303950e630 --- /dev/null +++ b/drivers/misc/habanalabs/common/mmu.c @@ -0,0 +1,1037 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright 2016-2019 HabanaLabs, Ltd. + * All Rights Reserved. + */ + +#include "habanalabs.h" +#include "include/hw_ip/mmu/mmu_general.h" + +#include +#include + +static inline u64 get_phys_addr(struct hl_ctx *ctx, u64 shadow_addr); + +static struct pgt_info *get_pgt_info(struct hl_ctx *ctx, u64 hop_addr) +{ + struct pgt_info *pgt_info = NULL; + + hash_for_each_possible(ctx->mmu_shadow_hash, pgt_info, node, + (unsigned long) hop_addr) + if (hop_addr == pgt_info->shadow_addr) + break; + + return pgt_info; +} + +static void _free_hop(struct hl_ctx *ctx, struct pgt_info *pgt_info) +{ + struct hl_device *hdev = ctx->hdev; + + gen_pool_free(hdev->mmu_pgt_pool, pgt_info->phys_addr, + hdev->asic_prop.mmu_hop_table_size); + hash_del(&pgt_info->node); + kfree((u64 *) (uintptr_t) pgt_info->shadow_addr); + kfree(pgt_info); +} + +static void free_hop(struct hl_ctx *ctx, u64 hop_addr) +{ + struct pgt_info *pgt_info = get_pgt_info(ctx, hop_addr); + + _free_hop(ctx, pgt_info); +} + +static u64 alloc_hop(struct hl_ctx *ctx) +{ + struct hl_device *hdev = ctx->hdev; + struct asic_fixed_properties *prop = &hdev->asic_prop; + struct pgt_info *pgt_info; + u64 phys_addr, shadow_addr; + + pgt_info = kmalloc(sizeof(*pgt_info), GFP_KERNEL); + if (!pgt_info) + return ULLONG_MAX; + + phys_addr = (u64) gen_pool_alloc(hdev->mmu_pgt_pool, + prop->mmu_hop_table_size); + if (!phys_addr) { + dev_err(hdev->dev, "failed to allocate page\n"); + goto pool_add_err; + } + + shadow_addr = (u64) (uintptr_t) kzalloc(prop->mmu_hop_table_size, + GFP_KERNEL); + if (!shadow_addr) + goto shadow_err; + + pgt_info->phys_addr = phys_addr; + pgt_info->shadow_addr = shadow_addr; + pgt_info->ctx = ctx; + pgt_info->num_of_ptes = 0; + hash_add(ctx->mmu_shadow_hash, &pgt_info->node, shadow_addr); + + return shadow_addr; + +shadow_err: + gen_pool_free(hdev->mmu_pgt_pool, phys_addr, prop->mmu_hop_table_size); +pool_add_err: + kfree(pgt_info); + + return ULLONG_MAX; +} + +static inline u64 get_phys_hop0_addr(struct hl_ctx *ctx) +{ + return ctx->hdev->asic_prop.mmu_pgt_addr + + (ctx->asid * ctx->hdev->asic_prop.mmu_hop_table_size); +} + +static inline u64 get_hop0_addr(struct hl_ctx *ctx) +{ + return (u64) (uintptr_t) ctx->hdev->mmu_shadow_hop0 + + (ctx->asid * ctx->hdev->asic_prop.mmu_hop_table_size); +} + +static inline void flush(struct hl_ctx *ctx) +{ + /* flush all writes from all cores to reach PCI */ + mb(); + ctx->hdev->asic_funcs->read_pte(ctx->hdev, get_phys_hop0_addr(ctx)); +} + +/* transform the value to physical address when writing to H/W */ +static inline void write_pte(struct hl_ctx *ctx, u64 shadow_pte_addr, u64 val) +{ + /* + * The value to write is actually the address of the next shadow hop + + * flags at the 12 LSBs. + * Hence in order to get the value to write to the physical PTE, we + * clear the 12 LSBs and translate the shadow hop to its associated + * physical hop, and add back the original 12 LSBs. + */ + u64 phys_val = get_phys_addr(ctx, val & HOP_PHYS_ADDR_MASK) | + (val & FLAGS_MASK); + + ctx->hdev->asic_funcs->write_pte(ctx->hdev, + get_phys_addr(ctx, shadow_pte_addr), + phys_val); + + *(u64 *) (uintptr_t) shadow_pte_addr = val; +} + +/* do not transform the value to physical address when writing to H/W */ +static inline void write_final_pte(struct hl_ctx *ctx, u64 shadow_pte_addr, + u64 val) +{ + ctx->hdev->asic_funcs->write_pte(ctx->hdev, + get_phys_addr(ctx, shadow_pte_addr), + val); + *(u64 *) (uintptr_t) shadow_pte_addr = val; +} + +/* clear the last and present bits */ +static inline void clear_pte(struct hl_ctx *ctx, u64 pte_addr) +{ + /* no need to transform the value to physical address */ + write_final_pte(ctx, pte_addr, 0); +} + +static inline void get_pte(struct hl_ctx *ctx, u64 hop_addr) +{ + get_pgt_info(ctx, hop_addr)->num_of_ptes++; +} + +/* + * put_pte - decrement the num of ptes and free the hop if possible + * + * @ctx: pointer to the context structure + * @hop_addr: addr of the hop + * + * This function returns the number of ptes left on this hop. If the number is + * 0, it means the pte was freed. + */ +static inline int put_pte(struct hl_ctx *ctx, u64 hop_addr) +{ + struct pgt_info *pgt_info = get_pgt_info(ctx, hop_addr); + int num_of_ptes_left; + + pgt_info->num_of_ptes--; + + /* + * Need to save the number of ptes left because free_hop might free + * the pgt_info + */ + num_of_ptes_left = pgt_info->num_of_ptes; + if (!num_of_ptes_left) + _free_hop(ctx, pgt_info); + + return num_of_ptes_left; +} + +static inline u64 get_hopN_pte_addr(struct hl_ctx *ctx, u64 hop_addr, + u64 virt_addr, u64 mask, u64 shift) +{ + return hop_addr + ctx->hdev->asic_prop.mmu_pte_size * + ((virt_addr & mask) >> shift); +} + +static inline u64 get_hop0_pte_addr(struct hl_ctx *ctx, + struct hl_mmu_properties *mmu_prop, + u64 hop_addr, u64 vaddr) +{ + return get_hopN_pte_addr(ctx, hop_addr, vaddr, mmu_prop->hop0_mask, + mmu_prop->hop0_shift); +} + +static inline u64 get_hop1_pte_addr(struct hl_ctx *ctx, + struct hl_mmu_properties *mmu_prop, + u64 hop_addr, u64 vaddr) +{ + return get_hopN_pte_addr(ctx, hop_addr, vaddr, mmu_prop->hop1_mask, + mmu_prop->hop1_shift); +} + +static inline u64 get_hop2_pte_addr(struct hl_ctx *ctx, + struct hl_mmu_properties *mmu_prop, + u64 hop_addr, u64 vaddr) +{ + return get_hopN_pte_addr(ctx, hop_addr, vaddr, mmu_prop->hop2_mask, + mmu_prop->hop2_shift); +} + +static inline u64 get_hop3_pte_addr(struct hl_ctx *ctx, + struct hl_mmu_properties *mmu_prop, + u64 hop_addr, u64 vaddr) +{ + return get_hopN_pte_addr(ctx, hop_addr, vaddr, mmu_prop->hop3_mask, + mmu_prop->hop3_shift); +} + +static inline u64 get_hop4_pte_addr(struct hl_ctx *ctx, + struct hl_mmu_properties *mmu_prop, + u64 hop_addr, u64 vaddr) +{ + return get_hopN_pte_addr(ctx, hop_addr, vaddr, mmu_prop->hop4_mask, + mmu_prop->hop4_shift); +} + +static inline u64 get_next_hop_addr(struct hl_ctx *ctx, u64 curr_pte) +{ + if (curr_pte & PAGE_PRESENT_MASK) + return curr_pte & HOP_PHYS_ADDR_MASK; + else + return ULLONG_MAX; +} + +static inline u64 get_alloc_next_hop_addr(struct hl_ctx *ctx, u64 curr_pte, + bool *is_new_hop) +{ + u64 hop_addr = get_next_hop_addr(ctx, curr_pte); + + if (hop_addr == ULLONG_MAX) { + hop_addr = alloc_hop(ctx); + *is_new_hop = (hop_addr != ULLONG_MAX); + } + + return hop_addr; +} + +/* translates shadow address inside hop to a physical address */ +static inline u64 get_phys_addr(struct hl_ctx *ctx, u64 shadow_addr) +{ + u64 page_mask = (ctx->hdev->asic_prop.mmu_hop_table_size - 1); + u64 shadow_hop_addr = shadow_addr & ~page_mask; + u64 pte_offset = shadow_addr & page_mask; + u64 phys_hop_addr; + + if (shadow_hop_addr != get_hop0_addr(ctx)) + phys_hop_addr = get_pgt_info(ctx, shadow_hop_addr)->phys_addr; + else + phys_hop_addr = get_phys_hop0_addr(ctx); + + return phys_hop_addr + pte_offset; +} + +static bool is_dram_va(struct hl_device *hdev, u64 virt_addr) +{ + struct asic_fixed_properties *prop = &hdev->asic_prop; + + return hl_mem_area_inside_range(virt_addr, prop->dmmu.page_size, + prop->dmmu.start_addr, + prop->dmmu.end_addr); +} + +static int dram_default_mapping_init(struct hl_ctx *ctx) +{ + struct hl_device *hdev = ctx->hdev; + struct asic_fixed_properties *prop = &hdev->asic_prop; + u64 num_of_hop3, total_hops, hop0_addr, hop1_addr, hop2_addr, + hop2_pte_addr, hop3_pte_addr, pte_val; + int rc, i, j, hop3_allocated = 0; + + if ((!hdev->dram_supports_virtual_memory) || + (!hdev->dram_default_page_mapping) || + (ctx->asid == HL_KERNEL_ASID_ID)) + return 0; + + num_of_hop3 = prop->dram_size_for_default_page_mapping; + do_div(num_of_hop3, prop->dram_page_size); + do_div(num_of_hop3, PTE_ENTRIES_IN_HOP); + + /* add hop1 and hop2 */ + total_hops = num_of_hop3 + 2; + + ctx->dram_default_hops = kzalloc(HL_PTE_SIZE * total_hops, GFP_KERNEL); + if (!ctx->dram_default_hops) + return -ENOMEM; + + hop0_addr = get_hop0_addr(ctx); + + hop1_addr = alloc_hop(ctx); + if (hop1_addr == ULLONG_MAX) { + dev_err(hdev->dev, "failed to alloc hop 1\n"); + rc = -ENOMEM; + goto hop1_err; + } + + ctx->dram_default_hops[total_hops - 1] = hop1_addr; + + hop2_addr = alloc_hop(ctx); + if (hop2_addr == ULLONG_MAX) { + dev_err(hdev->dev, "failed to alloc hop 2\n"); + rc = -ENOMEM; + goto hop2_err; + } + + ctx->dram_default_hops[total_hops - 2] = hop2_addr; + + for (i = 0 ; i < num_of_hop3 ; i++) { + ctx->dram_default_hops[i] = alloc_hop(ctx); + if (ctx->dram_default_hops[i] == ULLONG_MAX) { + dev_err(hdev->dev, "failed to alloc hop 3, i: %d\n", i); + rc = -ENOMEM; + goto hop3_err; + } + hop3_allocated++; + } + + /* need only pte 0 in hops 0 and 1 */ + pte_val = (hop1_addr & HOP_PHYS_ADDR_MASK) | PAGE_PRESENT_MASK; + write_pte(ctx, hop0_addr, pte_val); + + pte_val = (hop2_addr & HOP_PHYS_ADDR_MASK) | PAGE_PRESENT_MASK; + write_pte(ctx, hop1_addr, pte_val); + get_pte(ctx, hop1_addr); + + hop2_pte_addr = hop2_addr; + for (i = 0 ; i < num_of_hop3 ; i++) { + pte_val = (ctx->dram_default_hops[i] & HOP_PHYS_ADDR_MASK) | + PAGE_PRESENT_MASK; + write_pte(ctx, hop2_pte_addr, pte_val); + get_pte(ctx, hop2_addr); + hop2_pte_addr += HL_PTE_SIZE; + } + + pte_val = (prop->mmu_dram_default_page_addr & HOP_PHYS_ADDR_MASK) | + LAST_MASK | PAGE_PRESENT_MASK; + + for (i = 0 ; i < num_of_hop3 ; i++) { + hop3_pte_addr = ctx->dram_default_hops[i]; + for (j = 0 ; j < PTE_ENTRIES_IN_HOP ; j++) { + write_final_pte(ctx, hop3_pte_addr, pte_val); + get_pte(ctx, ctx->dram_default_hops[i]); + hop3_pte_addr += HL_PTE_SIZE; + } + } + + flush(ctx); + + return 0; + +hop3_err: + for (i = 0 ; i < hop3_allocated ; i++) + free_hop(ctx, ctx->dram_default_hops[i]); + + free_hop(ctx, hop2_addr); +hop2_err: + free_hop(ctx, hop1_addr); +hop1_err: + kfree(ctx->dram_default_hops); + + return rc; +} + +static void dram_default_mapping_fini(struct hl_ctx *ctx) +{ + struct hl_device *hdev = ctx->hdev; + struct asic_fixed_properties *prop = &hdev->asic_prop; + u64 num_of_hop3, total_hops, hop0_addr, hop1_addr, hop2_addr, + hop2_pte_addr, hop3_pte_addr; + int i, j; + + if ((!hdev->dram_supports_virtual_memory) || + (!hdev->dram_default_page_mapping) || + (ctx->asid == HL_KERNEL_ASID_ID)) + return; + + num_of_hop3 = prop->dram_size_for_default_page_mapping; + do_div(num_of_hop3, prop->dram_page_size); + do_div(num_of_hop3, PTE_ENTRIES_IN_HOP); + + hop0_addr = get_hop0_addr(ctx); + /* add hop1 and hop2 */ + total_hops = num_of_hop3 + 2; + hop1_addr = ctx->dram_default_hops[total_hops - 1]; + hop2_addr = ctx->dram_default_hops[total_hops - 2]; + + for (i = 0 ; i < num_of_hop3 ; i++) { + hop3_pte_addr = ctx->dram_default_hops[i]; + for (j = 0 ; j < PTE_ENTRIES_IN_HOP ; j++) { + clear_pte(ctx, hop3_pte_addr); + put_pte(ctx, ctx->dram_default_hops[i]); + hop3_pte_addr += HL_PTE_SIZE; + } + } + + hop2_pte_addr = hop2_addr; + hop2_pte_addr = hop2_addr; + for (i = 0 ; i < num_of_hop3 ; i++) { + clear_pte(ctx, hop2_pte_addr); + put_pte(ctx, hop2_addr); + hop2_pte_addr += HL_PTE_SIZE; + } + + clear_pte(ctx, hop1_addr); + put_pte(ctx, hop1_addr); + clear_pte(ctx, hop0_addr); + + kfree(ctx->dram_default_hops); + + flush(ctx); +} + +/** + * hl_mmu_init() - initialize the MMU module. + * @hdev: habanalabs device structure. + * + * This function does the following: + * - Create a pool of pages for pgt_infos. + * - Create a shadow table for pgt + * + * Return: 0 for success, non-zero for failure. + */ +int hl_mmu_init(struct hl_device *hdev) +{ + struct asic_fixed_properties *prop = &hdev->asic_prop; + int rc; + + if (!hdev->mmu_enable) + return 0; + + hdev->mmu_pgt_pool = + gen_pool_create(__ffs(prop->mmu_hop_table_size), -1); + + if (!hdev->mmu_pgt_pool) { + dev_err(hdev->dev, "Failed to create page gen pool\n"); + return -ENOMEM; + } + + rc = gen_pool_add(hdev->mmu_pgt_pool, prop->mmu_pgt_addr + + prop->mmu_hop0_tables_total_size, + prop->mmu_pgt_size - prop->mmu_hop0_tables_total_size, + -1); + if (rc) { + dev_err(hdev->dev, "Failed to add memory to page gen pool\n"); + goto err_pool_add; + } + + hdev->mmu_shadow_hop0 = kvmalloc_array(prop->max_asid, + prop->mmu_hop_table_size, + GFP_KERNEL | __GFP_ZERO); + if (!hdev->mmu_shadow_hop0) { + rc = -ENOMEM; + goto err_pool_add; + } + + /* MMU H/W init will be done in device hw_init() */ + + return 0; + +err_pool_add: + gen_pool_destroy(hdev->mmu_pgt_pool); + + return rc; +} + +/** + * hl_mmu_fini() - release the MMU module. + * @hdev: habanalabs device structure. + * + * This function does the following: + * - Disable MMU in H/W. + * - Free the pgt_infos pool. + * + * All contexts should be freed before calling this function. + */ +void hl_mmu_fini(struct hl_device *hdev) +{ + if (!hdev->mmu_enable) + return; + + /* MMU H/W fini was already done in device hw_fini() */ + + kvfree(hdev->mmu_shadow_hop0); + gen_pool_destroy(hdev->mmu_pgt_pool); +} + +/** + * hl_mmu_ctx_init() - initialize a context for using the MMU module. + * @ctx: pointer to the context structure to initialize. + * + * Initialize a mutex to protect the concurrent mapping flow, a hash to hold all + * page tables hops related to this context. + * Return: 0 on success, non-zero otherwise. + */ +int hl_mmu_ctx_init(struct hl_ctx *ctx) +{ + struct hl_device *hdev = ctx->hdev; + + if (!hdev->mmu_enable) + return 0; + + mutex_init(&ctx->mmu_lock); + hash_init(ctx->mmu_shadow_hash); + + return dram_default_mapping_init(ctx); +} + +/* + * hl_mmu_ctx_fini - disable a ctx from using the mmu module + * + * @ctx: pointer to the context structure + * + * This function does the following: + * - Free any pgts which were not freed yet + * - Free the mutex + * - Free DRAM default page mapping hops + */ +void hl_mmu_ctx_fini(struct hl_ctx *ctx) +{ + struct hl_device *hdev = ctx->hdev; + struct pgt_info *pgt_info; + struct hlist_node *tmp; + int i; + + if (!hdev->mmu_enable) + return; + + dram_default_mapping_fini(ctx); + + if (!hash_empty(ctx->mmu_shadow_hash)) + dev_err(hdev->dev, "ctx %d is freed while it has pgts in use\n", + ctx->asid); + + hash_for_each_safe(ctx->mmu_shadow_hash, i, tmp, pgt_info, node) { + dev_err_ratelimited(hdev->dev, + "pgt_info of addr 0x%llx of asid %d was not destroyed, num_ptes: %d\n", + pgt_info->phys_addr, ctx->asid, pgt_info->num_of_ptes); + _free_hop(ctx, pgt_info); + } + + mutex_destroy(&ctx->mmu_lock); +} + +static int _hl_mmu_unmap(struct hl_ctx *ctx, u64 virt_addr, bool is_dram_addr) +{ + struct hl_device *hdev = ctx->hdev; + struct asic_fixed_properties *prop = &hdev->asic_prop; + struct hl_mmu_properties *mmu_prop; + u64 hop0_addr = 0, hop0_pte_addr = 0, + hop1_addr = 0, hop1_pte_addr = 0, + hop2_addr = 0, hop2_pte_addr = 0, + hop3_addr = 0, hop3_pte_addr = 0, + hop4_addr = 0, hop4_pte_addr = 0, + curr_pte; + bool is_huge, clear_hop3 = true; + + /* shifts and masks are the same in PMMU and HPMMU, use one of them */ + mmu_prop = is_dram_addr ? &prop->dmmu : &prop->pmmu; + + hop0_addr = get_hop0_addr(ctx); + hop0_pte_addr = get_hop0_pte_addr(ctx, mmu_prop, hop0_addr, virt_addr); + + curr_pte = *(u64 *) (uintptr_t) hop0_pte_addr; + + hop1_addr = get_next_hop_addr(ctx, curr_pte); + + if (hop1_addr == ULLONG_MAX) + goto not_mapped; + + hop1_pte_addr = get_hop1_pte_addr(ctx, mmu_prop, hop1_addr, virt_addr); + + curr_pte = *(u64 *) (uintptr_t) hop1_pte_addr; + + hop2_addr = get_next_hop_addr(ctx, curr_pte); + + if (hop2_addr == ULLONG_MAX) + goto not_mapped; + + hop2_pte_addr = get_hop2_pte_addr(ctx, mmu_prop, hop2_addr, virt_addr); + + curr_pte = *(u64 *) (uintptr_t) hop2_pte_addr; + + hop3_addr = get_next_hop_addr(ctx, curr_pte); + + if (hop3_addr == ULLONG_MAX) + goto not_mapped; + + hop3_pte_addr = get_hop3_pte_addr(ctx, mmu_prop, hop3_addr, virt_addr); + + curr_pte = *(u64 *) (uintptr_t) hop3_pte_addr; + + is_huge = curr_pte & LAST_MASK; + + if (is_dram_addr && !is_huge) { + dev_err(hdev->dev, + "DRAM unmapping should use huge pages only\n"); + return -EFAULT; + } + + if (!is_huge) { + hop4_addr = get_next_hop_addr(ctx, curr_pte); + + if (hop4_addr == ULLONG_MAX) + goto not_mapped; + + hop4_pte_addr = get_hop4_pte_addr(ctx, mmu_prop, hop4_addr, + virt_addr); + + curr_pte = *(u64 *) (uintptr_t) hop4_pte_addr; + + clear_hop3 = false; + } + + if (hdev->dram_default_page_mapping && is_dram_addr) { + u64 default_pte = (prop->mmu_dram_default_page_addr & + HOP_PHYS_ADDR_MASK) | LAST_MASK | + PAGE_PRESENT_MASK; + if (curr_pte == default_pte) { + dev_err(hdev->dev, + "DRAM: hop3 PTE points to zero page, can't unmap, va: 0x%llx\n", + virt_addr); + goto not_mapped; + } + + if (!(curr_pte & PAGE_PRESENT_MASK)) { + dev_err(hdev->dev, + "DRAM: hop3 PTE is cleared! can't unmap, va: 0x%llx\n", + virt_addr); + goto not_mapped; + } + + write_final_pte(ctx, hop3_pte_addr, default_pte); + put_pte(ctx, hop3_addr); + } else { + if (!(curr_pte & PAGE_PRESENT_MASK)) + goto not_mapped; + + if (hop4_addr) + clear_pte(ctx, hop4_pte_addr); + else + clear_pte(ctx, hop3_pte_addr); + + if (hop4_addr && !put_pte(ctx, hop4_addr)) + clear_hop3 = true; + + if (!clear_hop3) + goto mapped; + + clear_pte(ctx, hop3_pte_addr); + + if (put_pte(ctx, hop3_addr)) + goto mapped; + + clear_pte(ctx, hop2_pte_addr); + + if (put_pte(ctx, hop2_addr)) + goto mapped; + + clear_pte(ctx, hop1_pte_addr); + + if (put_pte(ctx, hop1_addr)) + goto mapped; + + clear_pte(ctx, hop0_pte_addr); + } + +mapped: + return 0; + +not_mapped: + dev_err(hdev->dev, "virt addr 0x%llx is not mapped to phys addr\n", + virt_addr); + + return -EINVAL; +} + +/* + * hl_mmu_unmap - unmaps a virtual addr + * + * @ctx: pointer to the context structure + * @virt_addr: virt addr to map from + * @page_size: size of the page to unmap + * @flush_pte: whether to do a PCI flush + * + * This function does the following: + * - Check that the virt addr is mapped + * - Unmap the virt addr and frees pgts if possible + * - Returns 0 on success, -EINVAL if the given addr is not mapped + * + * Because this function changes the page tables in the device and because it + * changes the MMU hash, it must be protected by a lock. + * However, because it maps only a single page, the lock should be implemented + * in a higher level in order to protect the entire mapping of the memory area + * + * For optimization reasons PCI flush may be requested once after unmapping of + * large area. + */ +int hl_mmu_unmap(struct hl_ctx *ctx, u64 virt_addr, u32 page_size, + bool flush_pte) +{ + struct hl_device *hdev = ctx->hdev; + struct asic_fixed_properties *prop = &hdev->asic_prop; + struct hl_mmu_properties *mmu_prop; + u64 real_virt_addr; + u32 real_page_size, npages; + int i, rc = 0; + bool is_dram_addr; + + if (!hdev->mmu_enable) + return 0; + + is_dram_addr = is_dram_va(hdev, virt_addr); + + if (is_dram_addr) + mmu_prop = &prop->dmmu; + else if ((page_size % prop->pmmu_huge.page_size) == 0) + mmu_prop = &prop->pmmu_huge; + else + mmu_prop = &prop->pmmu; + + /* + * The H/W handles mapping of specific page sizes. Hence if the page + * size is bigger, we break it to sub-pages and unmap them separately. + */ + if ((page_size % mmu_prop->page_size) == 0) { + real_page_size = mmu_prop->page_size; + } else { + dev_err(hdev->dev, + "page size of %u is not %uKB aligned, can't unmap\n", + page_size, mmu_prop->page_size >> 10); + + return -EFAULT; + } + + npages = page_size / real_page_size; + real_virt_addr = virt_addr; + + for (i = 0 ; i < npages ; i++) { + rc = _hl_mmu_unmap(ctx, real_virt_addr, is_dram_addr); + if (rc) + break; + + real_virt_addr += real_page_size; + } + + if (flush_pte) + flush(ctx); + + return rc; +} + +static int _hl_mmu_map(struct hl_ctx *ctx, u64 virt_addr, u64 phys_addr, + u32 page_size, bool is_dram_addr) +{ + struct hl_device *hdev = ctx->hdev; + struct asic_fixed_properties *prop = &hdev->asic_prop; + struct hl_mmu_properties *mmu_prop; + u64 hop0_addr = 0, hop0_pte_addr = 0, + hop1_addr = 0, hop1_pte_addr = 0, + hop2_addr = 0, hop2_pte_addr = 0, + hop3_addr = 0, hop3_pte_addr = 0, + hop4_addr = 0, hop4_pte_addr = 0, + curr_pte = 0; + bool hop1_new = false, hop2_new = false, hop3_new = false, + hop4_new = false, is_huge; + int rc = -ENOMEM; + + /* + * This mapping function can map a page or a huge page. For huge page + * there are only 3 hops rather than 4. Currently the DRAM allocation + * uses huge pages only but user memory could have been allocated with + * one of the two page sizes. Since this is a common code for all the + * three cases, we need this hugs page check. + */ + if (is_dram_addr) { + mmu_prop = &prop->dmmu; + is_huge = true; + } else if (page_size == prop->pmmu_huge.page_size) { + mmu_prop = &prop->pmmu_huge; + is_huge = true; + } else { + mmu_prop = &prop->pmmu; + is_huge = false; + } + + hop0_addr = get_hop0_addr(ctx); + hop0_pte_addr = get_hop0_pte_addr(ctx, mmu_prop, hop0_addr, virt_addr); + curr_pte = *(u64 *) (uintptr_t) hop0_pte_addr; + + hop1_addr = get_alloc_next_hop_addr(ctx, curr_pte, &hop1_new); + if (hop1_addr == ULLONG_MAX) + goto err; + + hop1_pte_addr = get_hop1_pte_addr(ctx, mmu_prop, hop1_addr, virt_addr); + curr_pte = *(u64 *) (uintptr_t) hop1_pte_addr; + + hop2_addr = get_alloc_next_hop_addr(ctx, curr_pte, &hop2_new); + if (hop2_addr == ULLONG_MAX) + goto err; + + hop2_pte_addr = get_hop2_pte_addr(ctx, mmu_prop, hop2_addr, virt_addr); + curr_pte = *(u64 *) (uintptr_t) hop2_pte_addr; + + hop3_addr = get_alloc_next_hop_addr(ctx, curr_pte, &hop3_new); + if (hop3_addr == ULLONG_MAX) + goto err; + + hop3_pte_addr = get_hop3_pte_addr(ctx, mmu_prop, hop3_addr, virt_addr); + curr_pte = *(u64 *) (uintptr_t) hop3_pte_addr; + + if (!is_huge) { + hop4_addr = get_alloc_next_hop_addr(ctx, curr_pte, &hop4_new); + if (hop4_addr == ULLONG_MAX) + goto err; + + hop4_pte_addr = get_hop4_pte_addr(ctx, mmu_prop, hop4_addr, + virt_addr); + curr_pte = *(u64 *) (uintptr_t) hop4_pte_addr; + } + + if (hdev->dram_default_page_mapping && is_dram_addr) { + u64 default_pte = (prop->mmu_dram_default_page_addr & + HOP_PHYS_ADDR_MASK) | LAST_MASK | + PAGE_PRESENT_MASK; + + if (curr_pte != default_pte) { + dev_err(hdev->dev, + "DRAM: mapping already exists for virt_addr 0x%llx\n", + virt_addr); + rc = -EINVAL; + goto err; + } + + if (hop1_new || hop2_new || hop3_new || hop4_new) { + dev_err(hdev->dev, + "DRAM mapping should not allocate more hops\n"); + rc = -EFAULT; + goto err; + } + } else if (curr_pte & PAGE_PRESENT_MASK) { + dev_err(hdev->dev, + "mapping already exists for virt_addr 0x%llx\n", + virt_addr); + + dev_dbg(hdev->dev, "hop0 pte: 0x%llx (0x%llx)\n", + *(u64 *) (uintptr_t) hop0_pte_addr, hop0_pte_addr); + dev_dbg(hdev->dev, "hop1 pte: 0x%llx (0x%llx)\n", + *(u64 *) (uintptr_t) hop1_pte_addr, hop1_pte_addr); + dev_dbg(hdev->dev, "hop2 pte: 0x%llx (0x%llx)\n", + *(u64 *) (uintptr_t) hop2_pte_addr, hop2_pte_addr); + dev_dbg(hdev->dev, "hop3 pte: 0x%llx (0x%llx)\n", + *(u64 *) (uintptr_t) hop3_pte_addr, hop3_pte_addr); + + if (!is_huge) + dev_dbg(hdev->dev, "hop4 pte: 0x%llx (0x%llx)\n", + *(u64 *) (uintptr_t) hop4_pte_addr, + hop4_pte_addr); + + rc = -EINVAL; + goto err; + } + + curr_pte = (phys_addr & HOP_PHYS_ADDR_MASK) | LAST_MASK + | PAGE_PRESENT_MASK; + + if (is_huge) + write_final_pte(ctx, hop3_pte_addr, curr_pte); + else + write_final_pte(ctx, hop4_pte_addr, curr_pte); + + if (hop1_new) { + curr_pte = + (hop1_addr & HOP_PHYS_ADDR_MASK) | PAGE_PRESENT_MASK; + write_pte(ctx, hop0_pte_addr, curr_pte); + } + if (hop2_new) { + curr_pte = + (hop2_addr & HOP_PHYS_ADDR_MASK) | PAGE_PRESENT_MASK; + write_pte(ctx, hop1_pte_addr, curr_pte); + get_pte(ctx, hop1_addr); + } + if (hop3_new) { + curr_pte = + (hop3_addr & HOP_PHYS_ADDR_MASK) | PAGE_PRESENT_MASK; + write_pte(ctx, hop2_pte_addr, curr_pte); + get_pte(ctx, hop2_addr); + } + + if (!is_huge) { + if (hop4_new) { + curr_pte = (hop4_addr & HOP_PHYS_ADDR_MASK) | + PAGE_PRESENT_MASK; + write_pte(ctx, hop3_pte_addr, curr_pte); + get_pte(ctx, hop3_addr); + } + + get_pte(ctx, hop4_addr); + } else { + get_pte(ctx, hop3_addr); + } + + return 0; + +err: + if (hop4_new) + free_hop(ctx, hop4_addr); + if (hop3_new) + free_hop(ctx, hop3_addr); + if (hop2_new) + free_hop(ctx, hop2_addr); + if (hop1_new) + free_hop(ctx, hop1_addr); + + return rc; +} + +/* + * hl_mmu_map - maps a virtual addr to physical addr + * + * @ctx: pointer to the context structure + * @virt_addr: virt addr to map from + * @phys_addr: phys addr to map to + * @page_size: physical page size + * @flush_pte: whether to do a PCI flush + * + * This function does the following: + * - Check that the virt addr is not mapped + * - Allocate pgts as necessary in order to map the virt addr to the phys + * - Returns 0 on success, -EINVAL if addr is already mapped, or -ENOMEM. + * + * Because this function changes the page tables in the device and because it + * changes the MMU hash, it must be protected by a lock. + * However, because it maps only a single page, the lock should be implemented + * in a higher level in order to protect the entire mapping of the memory area + * + * For optimization reasons PCI flush may be requested once after mapping of + * large area. + */ +int hl_mmu_map(struct hl_ctx *ctx, u64 virt_addr, u64 phys_addr, u32 page_size, + bool flush_pte) +{ + struct hl_device *hdev = ctx->hdev; + struct asic_fixed_properties *prop = &hdev->asic_prop; + struct hl_mmu_properties *mmu_prop; + u64 real_virt_addr, real_phys_addr; + u32 real_page_size, npages; + int i, rc, mapped_cnt = 0; + bool is_dram_addr; + + if (!hdev->mmu_enable) + return 0; + + is_dram_addr = is_dram_va(hdev, virt_addr); + + if (is_dram_addr) + mmu_prop = &prop->dmmu; + else if ((page_size % prop->pmmu_huge.page_size) == 0) + mmu_prop = &prop->pmmu_huge; + else + mmu_prop = &prop->pmmu; + + /* + * The H/W handles mapping of specific page sizes. Hence if the page + * size is bigger, we break it to sub-pages and map them separately. + */ + if ((page_size % mmu_prop->page_size) == 0) { + real_page_size = mmu_prop->page_size; + } else { + dev_err(hdev->dev, + "page size of %u is not %uKB aligned, can't unmap\n", + page_size, mmu_prop->page_size >> 10); + + return -EFAULT; + } + + WARN_ONCE((phys_addr & (real_page_size - 1)), + "Mapping 0x%llx with page size of 0x%x is erroneous! Address must be divisible by page size", + phys_addr, real_page_size); + + npages = page_size / real_page_size; + real_virt_addr = virt_addr; + real_phys_addr = phys_addr; + + for (i = 0 ; i < npages ; i++) { + rc = _hl_mmu_map(ctx, real_virt_addr, real_phys_addr, + real_page_size, is_dram_addr); + if (rc) + goto err; + + real_virt_addr += real_page_size; + real_phys_addr += real_page_size; + mapped_cnt++; + } + + if (flush_pte) + flush(ctx); + + return 0; + +err: + real_virt_addr = virt_addr; + for (i = 0 ; i < mapped_cnt ; i++) { + if (_hl_mmu_unmap(ctx, real_virt_addr, is_dram_addr)) + dev_warn_ratelimited(hdev->dev, + "failed to unmap va: 0x%llx\n", real_virt_addr); + + real_virt_addr += real_page_size; + } + + flush(ctx); + + return rc; +} + +/* + * hl_mmu_swap_out - marks all mapping of the given ctx as swapped out + * + * @ctx: pointer to the context structure + * + */ +void hl_mmu_swap_out(struct hl_ctx *ctx) +{ + +} + +/* + * hl_mmu_swap_in - marks all mapping of the given ctx as swapped in + * + * @ctx: pointer to the context structure + * + */ +void hl_mmu_swap_in(struct hl_ctx *ctx) +{ + +} diff --git a/drivers/misc/habanalabs/common/pci.c b/drivers/misc/habanalabs/common/pci.c new file mode 100644 index 000000000000..1791f6623c69 --- /dev/null +++ b/drivers/misc/habanalabs/common/pci.c @@ -0,0 +1,400 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright 2016-2019 HabanaLabs, Ltd. + * All Rights Reserved. + */ + +#include "habanalabs.h" +#include "include/hw_ip/pci/pci_general.h" + +#include +#include + +#define HL_PLDM_PCI_ELBI_TIMEOUT_MSEC (HL_PCI_ELBI_TIMEOUT_MSEC * 10) + +#define IATU_REGION_CTRL_REGION_EN_MASK BIT(31) +#define IATU_REGION_CTRL_MATCH_MODE_MASK BIT(30) +#define IATU_REGION_CTRL_NUM_MATCH_EN_MASK BIT(19) +#define IATU_REGION_CTRL_BAR_NUM_MASK GENMASK(10, 8) + +/** + * hl_pci_bars_map() - Map PCI BARs. + * @hdev: Pointer to hl_device structure. + * @name: Array of BAR names. + * @is_wc: Array with flag per BAR whether a write-combined mapping is needed. + * + * Request PCI regions and map them to kernel virtual addresses. + * + * Return: 0 on success, non-zero for failure. + */ +int hl_pci_bars_map(struct hl_device *hdev, const char * const name[3], + bool is_wc[3]) +{ + struct pci_dev *pdev = hdev->pdev; + int rc, i, bar; + + rc = pci_request_regions(pdev, HL_NAME); + if (rc) { + dev_err(hdev->dev, "Cannot obtain PCI resources\n"); + return rc; + } + + for (i = 0 ; i < 3 ; i++) { + bar = i * 2; /* 64-bit BARs */ + hdev->pcie_bar[bar] = is_wc[i] ? + pci_ioremap_wc_bar(pdev, bar) : + pci_ioremap_bar(pdev, bar); + if (!hdev->pcie_bar[bar]) { + dev_err(hdev->dev, "pci_ioremap%s_bar failed for %s\n", + is_wc[i] ? "_wc" : "", name[i]); + rc = -ENODEV; + goto err; + } + } + + return 0; + +err: + for (i = 2 ; i >= 0 ; i--) { + bar = i * 2; /* 64-bit BARs */ + if (hdev->pcie_bar[bar]) + iounmap(hdev->pcie_bar[bar]); + } + + pci_release_regions(pdev); + + return rc; +} + +/** + * hl_pci_bars_unmap() - Unmap PCI BARS. + * @hdev: Pointer to hl_device structure. + * + * Release all PCI BARs and unmap their virtual addresses. + */ +static void hl_pci_bars_unmap(struct hl_device *hdev) +{ + struct pci_dev *pdev = hdev->pdev; + int i, bar; + + for (i = 2 ; i >= 0 ; i--) { + bar = i * 2; /* 64-bit BARs */ + iounmap(hdev->pcie_bar[bar]); + } + + pci_release_regions(pdev); +} + +/** + * hl_pci_elbi_write() - Write through the ELBI interface. + * @hdev: Pointer to hl_device structure. + * @addr: Address to write to + * @data: Data to write + * + * Return: 0 on success, negative value for failure. + */ +static int hl_pci_elbi_write(struct hl_device *hdev, u64 addr, u32 data) +{ + struct pci_dev *pdev = hdev->pdev; + ktime_t timeout; + u64 msec; + u32 val; + + if (hdev->pldm) + msec = HL_PLDM_PCI_ELBI_TIMEOUT_MSEC; + else + msec = HL_PCI_ELBI_TIMEOUT_MSEC; + + /* Clear previous status */ + pci_write_config_dword(pdev, mmPCI_CONFIG_ELBI_STS, 0); + + pci_write_config_dword(pdev, mmPCI_CONFIG_ELBI_ADDR, (u32) addr); + pci_write_config_dword(pdev, mmPCI_CONFIG_ELBI_DATA, data); + pci_write_config_dword(pdev, mmPCI_CONFIG_ELBI_CTRL, + PCI_CONFIG_ELBI_CTRL_WRITE); + + timeout = ktime_add_ms(ktime_get(), msec); + for (;;) { + pci_read_config_dword(pdev, mmPCI_CONFIG_ELBI_STS, &val); + if (val & PCI_CONFIG_ELBI_STS_MASK) + break; + if (ktime_compare(ktime_get(), timeout) > 0) { + pci_read_config_dword(pdev, mmPCI_CONFIG_ELBI_STS, + &val); + break; + } + + usleep_range(300, 500); + } + + if ((val & PCI_CONFIG_ELBI_STS_MASK) == PCI_CONFIG_ELBI_STS_DONE) + return 0; + + if (val & PCI_CONFIG_ELBI_STS_ERR) { + dev_err(hdev->dev, "Error writing to ELBI\n"); + return -EIO; + } + + if (!(val & PCI_CONFIG_ELBI_STS_MASK)) { + dev_err(hdev->dev, "ELBI write didn't finish in time\n"); + return -EIO; + } + + dev_err(hdev->dev, "ELBI write has undefined bits in status\n"); + return -EIO; +} + +/** + * hl_pci_iatu_write() - iatu write routine. + * @hdev: Pointer to hl_device structure. + * @addr: Address to write to + * @data: Data to write + * + * Return: 0 on success, negative value for failure. + */ +int hl_pci_iatu_write(struct hl_device *hdev, u32 addr, u32 data) +{ + struct asic_fixed_properties *prop = &hdev->asic_prop; + u32 dbi_offset; + int rc; + + dbi_offset = addr & 0xFFF; + + rc = hl_pci_elbi_write(hdev, prop->pcie_aux_dbi_reg_addr, 0x00300000); + rc |= hl_pci_elbi_write(hdev, prop->pcie_dbi_base_address + dbi_offset, + data); + + if (rc) + return -EIO; + + return 0; +} + +/** + * hl_pci_reset_link_through_bridge() - Reset PCI link. + * @hdev: Pointer to hl_device structure. + */ +static void hl_pci_reset_link_through_bridge(struct hl_device *hdev) +{ + struct pci_dev *pdev = hdev->pdev; + struct pci_dev *parent_port; + u16 val; + + parent_port = pdev->bus->self; + pci_read_config_word(parent_port, PCI_BRIDGE_CONTROL, &val); + val |= PCI_BRIDGE_CTL_BUS_RESET; + pci_write_config_word(parent_port, PCI_BRIDGE_CONTROL, val); + ssleep(1); + + val &= ~(PCI_BRIDGE_CTL_BUS_RESET); + pci_write_config_word(parent_port, PCI_BRIDGE_CONTROL, val); + ssleep(3); +} + +/** + * hl_pci_set_inbound_region() - Configure inbound region + * @hdev: Pointer to hl_device structure. + * @region: Inbound region number. + * @pci_region: Inbound region parameters. + * + * Configure the iATU inbound region. + * + * Return: 0 on success, negative value for failure. + */ +int hl_pci_set_inbound_region(struct hl_device *hdev, u8 region, + struct hl_inbound_pci_region *pci_region) +{ + struct asic_fixed_properties *prop = &hdev->asic_prop; + u64 bar_phys_base, region_base, region_end_address; + u32 offset, ctrl_reg_val; + int rc = 0; + + /* region offset */ + offset = (0x200 * region) + 0x100; + + if (pci_region->mode == PCI_ADDRESS_MATCH_MODE) { + bar_phys_base = hdev->pcie_bar_phys[pci_region->bar]; + region_base = bar_phys_base + pci_region->offset_in_bar; + region_end_address = region_base + pci_region->size - 1; + + rc |= hl_pci_iatu_write(hdev, offset + 0x8, + lower_32_bits(region_base)); + rc |= hl_pci_iatu_write(hdev, offset + 0xC, + upper_32_bits(region_base)); + rc |= hl_pci_iatu_write(hdev, offset + 0x10, + lower_32_bits(region_end_address)); + } + + /* Point to the specified address */ + rc = hl_pci_iatu_write(hdev, offset + 0x14, + lower_32_bits(pci_region->addr)); + rc |= hl_pci_iatu_write(hdev, offset + 0x18, + upper_32_bits(pci_region->addr)); + rc |= hl_pci_iatu_write(hdev, offset + 0x0, 0); + + /* Enable + bar/address match + match enable + bar number */ + ctrl_reg_val = FIELD_PREP(IATU_REGION_CTRL_REGION_EN_MASK, 1); + ctrl_reg_val |= FIELD_PREP(IATU_REGION_CTRL_MATCH_MODE_MASK, + pci_region->mode); + ctrl_reg_val |= FIELD_PREP(IATU_REGION_CTRL_NUM_MATCH_EN_MASK, 1); + + if (pci_region->mode == PCI_BAR_MATCH_MODE) + ctrl_reg_val |= FIELD_PREP(IATU_REGION_CTRL_BAR_NUM_MASK, + pci_region->bar); + + rc |= hl_pci_iatu_write(hdev, offset + 0x4, ctrl_reg_val); + + /* Return the DBI window to the default location */ + rc |= hl_pci_elbi_write(hdev, prop->pcie_aux_dbi_reg_addr, 0); + rc |= hl_pci_elbi_write(hdev, prop->pcie_aux_dbi_reg_addr + 4, 0); + + if (rc) + dev_err(hdev->dev, "failed to map bar %u to 0x%08llx\n", + pci_region->bar, pci_region->addr); + + return rc; +} + +/** + * hl_pci_set_outbound_region() - Configure outbound region 0 + * @hdev: Pointer to hl_device structure. + * @pci_region: Outbound region parameters. + * + * Configure the iATU outbound region 0. + * + * Return: 0 on success, negative value for failure. + */ +int hl_pci_set_outbound_region(struct hl_device *hdev, + struct hl_outbound_pci_region *pci_region) +{ + struct asic_fixed_properties *prop = &hdev->asic_prop; + u64 outbound_region_end_address; + int rc = 0; + + /* Outbound Region 0 */ + outbound_region_end_address = + pci_region->addr + pci_region->size - 1; + rc |= hl_pci_iatu_write(hdev, 0x008, + lower_32_bits(pci_region->addr)); + rc |= hl_pci_iatu_write(hdev, 0x00C, + upper_32_bits(pci_region->addr)); + rc |= hl_pci_iatu_write(hdev, 0x010, + lower_32_bits(outbound_region_end_address)); + rc |= hl_pci_iatu_write(hdev, 0x014, 0); + + if ((hdev->power9_64bit_dma_enable) && (hdev->dma_mask == 64)) + rc |= hl_pci_iatu_write(hdev, 0x018, 0x08000000); + else + rc |= hl_pci_iatu_write(hdev, 0x018, 0); + + rc |= hl_pci_iatu_write(hdev, 0x020, + upper_32_bits(outbound_region_end_address)); + /* Increase region size */ + rc |= hl_pci_iatu_write(hdev, 0x000, 0x00002000); + /* Enable */ + rc |= hl_pci_iatu_write(hdev, 0x004, 0x80000000); + + /* Return the DBI window to the default location */ + rc |= hl_pci_elbi_write(hdev, prop->pcie_aux_dbi_reg_addr, 0); + rc |= hl_pci_elbi_write(hdev, prop->pcie_aux_dbi_reg_addr + 4, 0); + + return rc; +} + +/** + * hl_pci_set_dma_mask() - Set DMA masks for the device. + * @hdev: Pointer to hl_device structure. + * + * This function sets the DMA masks (regular and consistent) for a specified + * value. If it doesn't succeed, it tries to set it to a fall-back value + * + * Return: 0 on success, non-zero for failure. + */ +static int hl_pci_set_dma_mask(struct hl_device *hdev) +{ + struct pci_dev *pdev = hdev->pdev; + int rc; + + /* set DMA mask */ + rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(hdev->dma_mask)); + if (rc) { + dev_err(hdev->dev, + "Failed to set pci dma mask to %d bits, error %d\n", + hdev->dma_mask, rc); + return rc; + } + + rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(hdev->dma_mask)); + if (rc) { + dev_err(hdev->dev, + "Failed to set pci consistent dma mask to %d bits, error %d\n", + hdev->dma_mask, rc); + return rc; + } + + return 0; +} + +/** + * hl_pci_init() - PCI initialization code. + * @hdev: Pointer to hl_device structure. + * + * Set DMA masks, initialize the PCI controller and map the PCI BARs. + * + * Return: 0 on success, non-zero for failure. + */ +int hl_pci_init(struct hl_device *hdev) +{ + struct pci_dev *pdev = hdev->pdev; + int rc; + + if (hdev->reset_pcilink) + hl_pci_reset_link_through_bridge(hdev); + + rc = pci_enable_device_mem(pdev); + if (rc) { + dev_err(hdev->dev, "can't enable PCI device\n"); + return rc; + } + + pci_set_master(pdev); + + rc = hdev->asic_funcs->pci_bars_map(hdev); + if (rc) { + dev_err(hdev->dev, "Failed to initialize PCI BARs\n"); + goto disable_device; + } + + rc = hdev->asic_funcs->init_iatu(hdev); + if (rc) { + dev_err(hdev->dev, "Failed to initialize iATU\n"); + goto disable_device; + } + + rc = hl_pci_set_dma_mask(hdev); + if (rc) + goto disable_device; + + return 0; + +disable_device: + pci_clear_master(pdev); + pci_disable_device(pdev); + + return rc; +} + +/** + * hl_fw_fini() - PCI finalization code. + * @hdev: Pointer to hl_device structure + * + * Unmap PCI bars and disable PCI device. + */ +void hl_pci_fini(struct hl_device *hdev) +{ + hl_pci_bars_unmap(hdev); + + pci_clear_master(hdev->pdev); + pci_disable_device(hdev->pdev); +} diff --git a/drivers/misc/habanalabs/common/sysfs.c b/drivers/misc/habanalabs/common/sysfs.c new file mode 100644 index 000000000000..5d78d5e1c782 --- /dev/null +++ b/drivers/misc/habanalabs/common/sysfs.c @@ -0,0 +1,442 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright 2016-2019 HabanaLabs, Ltd. + * All Rights Reserved. + */ + +#include "habanalabs.h" + +#include + +#define SET_CLK_PKT_TIMEOUT 1000000 /* 1s */ +#define SET_PWR_PKT_TIMEOUT 1000000 /* 1s */ + +long hl_get_frequency(struct hl_device *hdev, u32 pll_index, bool curr) +{ + struct armcp_packet pkt; + long result; + int rc; + + memset(&pkt, 0, sizeof(pkt)); + + if (curr) + pkt.ctl = cpu_to_le32(ARMCP_PACKET_FREQUENCY_CURR_GET << + ARMCP_PKT_CTL_OPCODE_SHIFT); + else + pkt.ctl = cpu_to_le32(ARMCP_PACKET_FREQUENCY_GET << + ARMCP_PKT_CTL_OPCODE_SHIFT); + pkt.pll_index = cpu_to_le32(pll_index); + + rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), + SET_CLK_PKT_TIMEOUT, &result); + + if (rc) { + dev_err(hdev->dev, + "Failed to get frequency of PLL %d, error %d\n", + pll_index, rc); + result = rc; + } + + return result; +} + +void hl_set_frequency(struct hl_device *hdev, u32 pll_index, u64 freq) +{ + struct armcp_packet pkt; + int rc; + + memset(&pkt, 0, sizeof(pkt)); + + pkt.ctl = cpu_to_le32(ARMCP_PACKET_FREQUENCY_SET << + ARMCP_PKT_CTL_OPCODE_SHIFT); + pkt.pll_index = cpu_to_le32(pll_index); + pkt.value = cpu_to_le64(freq); + + rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), + SET_CLK_PKT_TIMEOUT, NULL); + + if (rc) + dev_err(hdev->dev, + "Failed to set frequency to PLL %d, error %d\n", + pll_index, rc); +} + +u64 hl_get_max_power(struct hl_device *hdev) +{ + struct armcp_packet pkt; + long result; + int rc; + + memset(&pkt, 0, sizeof(pkt)); + + pkt.ctl = cpu_to_le32(ARMCP_PACKET_MAX_POWER_GET << + ARMCP_PKT_CTL_OPCODE_SHIFT); + + rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), + SET_PWR_PKT_TIMEOUT, &result); + + if (rc) { + dev_err(hdev->dev, "Failed to get max power, error %d\n", rc); + result = rc; + } + + return result; +} + +void hl_set_max_power(struct hl_device *hdev, u64 value) +{ + struct armcp_packet pkt; + int rc; + + memset(&pkt, 0, sizeof(pkt)); + + pkt.ctl = cpu_to_le32(ARMCP_PACKET_MAX_POWER_SET << + ARMCP_PKT_CTL_OPCODE_SHIFT); + pkt.value = cpu_to_le64(value); + + rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), + SET_PWR_PKT_TIMEOUT, NULL); + + if (rc) + dev_err(hdev->dev, "Failed to set max power, error %d\n", rc); +} + +static ssize_t uboot_ver_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct hl_device *hdev = dev_get_drvdata(dev); + + return sprintf(buf, "%s\n", hdev->asic_prop.uboot_ver); +} + +static ssize_t armcp_kernel_ver_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct hl_device *hdev = dev_get_drvdata(dev); + + return sprintf(buf, "%s", hdev->asic_prop.armcp_info.kernel_version); +} + +static ssize_t armcp_ver_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct hl_device *hdev = dev_get_drvdata(dev); + + return sprintf(buf, "%s\n", hdev->asic_prop.armcp_info.armcp_version); +} + +static ssize_t cpld_ver_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct hl_device *hdev = dev_get_drvdata(dev); + + return sprintf(buf, "0x%08x\n", + hdev->asic_prop.armcp_info.cpld_version); +} + +static ssize_t infineon_ver_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct hl_device *hdev = dev_get_drvdata(dev); + + return sprintf(buf, "0x%04x\n", + hdev->asic_prop.armcp_info.infineon_version); +} + +static ssize_t fuse_ver_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct hl_device *hdev = dev_get_drvdata(dev); + + return sprintf(buf, "%s\n", hdev->asic_prop.armcp_info.fuse_version); +} + +static ssize_t thermal_ver_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct hl_device *hdev = dev_get_drvdata(dev); + + return sprintf(buf, "%s", hdev->asic_prop.armcp_info.thermal_version); +} + +static ssize_t preboot_btl_ver_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct hl_device *hdev = dev_get_drvdata(dev); + + return sprintf(buf, "%s\n", hdev->asic_prop.preboot_ver); +} + +static ssize_t soft_reset_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + struct hl_device *hdev = dev_get_drvdata(dev); + long value; + int rc; + + rc = kstrtoul(buf, 0, &value); + + if (rc) { + count = -EINVAL; + goto out; + } + + if (!hdev->supports_soft_reset) { + dev_err(hdev->dev, "Device does not support soft-reset\n"); + goto out; + } + + dev_warn(hdev->dev, "Soft-Reset requested through sysfs\n"); + + hl_device_reset(hdev, false, false); + +out: + return count; +} + +static ssize_t hard_reset_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct hl_device *hdev = dev_get_drvdata(dev); + long value; + int rc; + + rc = kstrtoul(buf, 0, &value); + + if (rc) { + count = -EINVAL; + goto out; + } + + dev_warn(hdev->dev, "Hard-Reset requested through sysfs\n"); + + hl_device_reset(hdev, true, false); + +out: + return count; +} + +static ssize_t device_type_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct hl_device *hdev = dev_get_drvdata(dev); + char *str; + + switch (hdev->asic_type) { + case ASIC_GOYA: + str = "GOYA"; + break; + case ASIC_GAUDI: + str = "GAUDI"; + break; + default: + dev_err(hdev->dev, "Unrecognized ASIC type %d\n", + hdev->asic_type); + return -EINVAL; + } + + return sprintf(buf, "%s\n", str); +} + +static ssize_t pci_addr_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct hl_device *hdev = dev_get_drvdata(dev); + + return sprintf(buf, "%04x:%02x:%02x.%x\n", + pci_domain_nr(hdev->pdev->bus), + hdev->pdev->bus->number, + PCI_SLOT(hdev->pdev->devfn), + PCI_FUNC(hdev->pdev->devfn)); +} + +static ssize_t status_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct hl_device *hdev = dev_get_drvdata(dev); + char *str; + + if (atomic_read(&hdev->in_reset)) + str = "In reset"; + else if (hdev->disabled) + str = "Malfunction"; + else + str = "Operational"; + + return sprintf(buf, "%s\n", str); +} + +static ssize_t soft_reset_cnt_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct hl_device *hdev = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", hdev->soft_reset_cnt); +} + +static ssize_t hard_reset_cnt_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct hl_device *hdev = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", hdev->hard_reset_cnt); +} + +static ssize_t max_power_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct hl_device *hdev = dev_get_drvdata(dev); + long val; + + if (hl_device_disabled_or_in_reset(hdev)) + return -ENODEV; + + val = hl_get_max_power(hdev); + + return sprintf(buf, "%lu\n", val); +} + +static ssize_t max_power_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct hl_device *hdev = dev_get_drvdata(dev); + unsigned long value; + int rc; + + if (hl_device_disabled_or_in_reset(hdev)) { + count = -ENODEV; + goto out; + } + + rc = kstrtoul(buf, 0, &value); + + if (rc) { + count = -EINVAL; + goto out; + } + + hdev->max_power = value; + hl_set_max_power(hdev, value); + +out: + return count; +} + +static ssize_t eeprom_read_handler(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, char *buf, loff_t offset, + size_t max_size) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct hl_device *hdev = dev_get_drvdata(dev); + char *data; + int rc; + + if (!max_size) + return -EINVAL; + + data = kzalloc(max_size, GFP_KERNEL); + if (!data) + return -ENOMEM; + + rc = hdev->asic_funcs->get_eeprom_data(hdev, data, max_size); + if (rc) + goto out; + + memcpy(buf, data, max_size); + +out: + kfree(data); + + return max_size; +} + +static DEVICE_ATTR_RO(armcp_kernel_ver); +static DEVICE_ATTR_RO(armcp_ver); +static DEVICE_ATTR_RO(cpld_ver); +static DEVICE_ATTR_RO(device_type); +static DEVICE_ATTR_RO(fuse_ver); +static DEVICE_ATTR_WO(hard_reset); +static DEVICE_ATTR_RO(hard_reset_cnt); +static DEVICE_ATTR_RO(infineon_ver); +static DEVICE_ATTR_RW(max_power); +static DEVICE_ATTR_RO(pci_addr); +static DEVICE_ATTR_RO(preboot_btl_ver); +static DEVICE_ATTR_WO(soft_reset); +static DEVICE_ATTR_RO(soft_reset_cnt); +static DEVICE_ATTR_RO(status); +static DEVICE_ATTR_RO(thermal_ver); +static DEVICE_ATTR_RO(uboot_ver); + +static struct bin_attribute bin_attr_eeprom = { + .attr = {.name = "eeprom", .mode = (0444)}, + .size = PAGE_SIZE, + .read = eeprom_read_handler +}; + +static struct attribute *hl_dev_attrs[] = { + &dev_attr_armcp_kernel_ver.attr, + &dev_attr_armcp_ver.attr, + &dev_attr_cpld_ver.attr, + &dev_attr_device_type.attr, + &dev_attr_fuse_ver.attr, + &dev_attr_hard_reset.attr, + &dev_attr_hard_reset_cnt.attr, + &dev_attr_infineon_ver.attr, + &dev_attr_max_power.attr, + &dev_attr_pci_addr.attr, + &dev_attr_preboot_btl_ver.attr, + &dev_attr_soft_reset.attr, + &dev_attr_soft_reset_cnt.attr, + &dev_attr_status.attr, + &dev_attr_thermal_ver.attr, + &dev_attr_uboot_ver.attr, + NULL, +}; + +static struct bin_attribute *hl_dev_bin_attrs[] = { + &bin_attr_eeprom, + NULL +}; + +static struct attribute_group hl_dev_attr_group = { + .attrs = hl_dev_attrs, + .bin_attrs = hl_dev_bin_attrs, +}; + +static struct attribute_group hl_dev_clks_attr_group; + +static const struct attribute_group *hl_dev_attr_groups[] = { + &hl_dev_attr_group, + &hl_dev_clks_attr_group, + NULL, +}; + +int hl_sysfs_init(struct hl_device *hdev) +{ + int rc; + + if (hdev->asic_type == ASIC_GOYA) + hdev->pm_mng_profile = PM_AUTO; + else + hdev->pm_mng_profile = PM_MANUAL; + hdev->max_power = hdev->asic_prop.max_power_default; + + hdev->asic_funcs->add_device_attr(hdev, &hl_dev_clks_attr_group); + + rc = device_add_groups(hdev->dev, hl_dev_attr_groups); + if (rc) { + dev_err(hdev->dev, + "Failed to add groups to device, error %d\n", rc); + return rc; + } + + return 0; +} + +void hl_sysfs_fini(struct hl_device *hdev) +{ + device_remove_groups(hdev->dev, hl_dev_attr_groups); +} diff --git a/drivers/misc/habanalabs/context.c b/drivers/misc/habanalabs/context.c deleted file mode 100644 index 1e3e5b19ecd9..000000000000 --- a/drivers/misc/habanalabs/context.c +++ /dev/null @@ -1,237 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -/* - * Copyright 2016-2019 HabanaLabs, Ltd. - * All Rights Reserved. - */ - -#include "habanalabs.h" - -#include - -static void hl_ctx_fini(struct hl_ctx *ctx) -{ - struct hl_device *hdev = ctx->hdev; - int i; - - /* - * If we arrived here, there are no jobs waiting for this context - * on its queues so we can safely remove it. - * This is because for each CS, we increment the ref count and for - * every CS that was finished we decrement it and we won't arrive - * to this function unless the ref count is 0 - */ - - for (i = 0 ; i < hdev->asic_prop.max_pending_cs ; i++) - dma_fence_put(ctx->cs_pending[i]); - - kfree(ctx->cs_pending); - - if (ctx->asid != HL_KERNEL_ASID_ID) { - /* The engines are stopped as there is no executing CS, but the - * Coresight might be still working by accessing addresses - * related to the stopped engines. Hence stop it explicitly. - * Stop only if this is the compute context, as there can be - * only one compute context - */ - if ((hdev->in_debug) && (hdev->compute_ctx == ctx)) - hl_device_set_debug_mode(hdev, false); - - hl_vm_ctx_fini(ctx); - hl_asid_free(hdev, ctx->asid); - } else { - hl_mmu_ctx_fini(ctx); - } -} - -void hl_ctx_do_release(struct kref *ref) -{ - struct hl_ctx *ctx; - - ctx = container_of(ref, struct hl_ctx, refcount); - - hl_ctx_fini(ctx); - - if (ctx->hpriv) - hl_hpriv_put(ctx->hpriv); - - kfree(ctx); -} - -int hl_ctx_create(struct hl_device *hdev, struct hl_fpriv *hpriv) -{ - struct hl_ctx_mgr *mgr = &hpriv->ctx_mgr; - struct hl_ctx *ctx; - int rc; - - ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); - if (!ctx) { - rc = -ENOMEM; - goto out_err; - } - - mutex_lock(&mgr->ctx_lock); - rc = idr_alloc(&mgr->ctx_handles, ctx, 1, 0, GFP_KERNEL); - mutex_unlock(&mgr->ctx_lock); - - if (rc < 0) { - dev_err(hdev->dev, "Failed to allocate IDR for a new CTX\n"); - goto free_ctx; - } - - ctx->handle = rc; - - rc = hl_ctx_init(hdev, ctx, false); - if (rc) - goto remove_from_idr; - - hl_hpriv_get(hpriv); - ctx->hpriv = hpriv; - - /* TODO: remove for multiple contexts per process */ - hpriv->ctx = ctx; - - /* TODO: remove the following line for multiple process support */ - hdev->compute_ctx = ctx; - - return 0; - -remove_from_idr: - mutex_lock(&mgr->ctx_lock); - idr_remove(&mgr->ctx_handles, ctx->handle); - mutex_unlock(&mgr->ctx_lock); -free_ctx: - kfree(ctx); -out_err: - return rc; -} - -void hl_ctx_free(struct hl_device *hdev, struct hl_ctx *ctx) -{ - if (kref_put(&ctx->refcount, hl_ctx_do_release) == 1) - return; - - dev_warn(hdev->dev, - "user process released device but its command submissions are still executing\n"); -} - -int hl_ctx_init(struct hl_device *hdev, struct hl_ctx *ctx, bool is_kernel_ctx) -{ - int rc = 0; - - ctx->hdev = hdev; - - kref_init(&ctx->refcount); - - ctx->cs_sequence = 1; - spin_lock_init(&ctx->cs_lock); - atomic_set(&ctx->thread_ctx_switch_token, 1); - ctx->thread_ctx_switch_wait_token = 0; - ctx->cs_pending = kcalloc(hdev->asic_prop.max_pending_cs, - sizeof(struct dma_fence *), - GFP_KERNEL); - if (!ctx->cs_pending) - return -ENOMEM; - - if (is_kernel_ctx) { - ctx->asid = HL_KERNEL_ASID_ID; /* Kernel driver gets ASID 0 */ - rc = hl_mmu_ctx_init(ctx); - if (rc) { - dev_err(hdev->dev, "Failed to init mmu ctx module\n"); - goto mem_ctx_err; - } - } else { - ctx->asid = hl_asid_alloc(hdev); - if (!ctx->asid) { - dev_err(hdev->dev, "No free ASID, failed to create context\n"); - return -ENOMEM; - } - - rc = hl_vm_ctx_init(ctx); - if (rc) { - dev_err(hdev->dev, "Failed to init mem ctx module\n"); - rc = -ENOMEM; - goto mem_ctx_err; - } - } - - return 0; - -mem_ctx_err: - if (ctx->asid != HL_KERNEL_ASID_ID) - hl_asid_free(hdev, ctx->asid); - - return rc; -} - -void hl_ctx_get(struct hl_device *hdev, struct hl_ctx *ctx) -{ - kref_get(&ctx->refcount); -} - -int hl_ctx_put(struct hl_ctx *ctx) -{ - return kref_put(&ctx->refcount, hl_ctx_do_release); -} - -struct dma_fence *hl_ctx_get_fence(struct hl_ctx *ctx, u64 seq) -{ - struct asic_fixed_properties *asic_prop = &ctx->hdev->asic_prop; - struct dma_fence *fence; - - spin_lock(&ctx->cs_lock); - - if (seq >= ctx->cs_sequence) { - spin_unlock(&ctx->cs_lock); - return ERR_PTR(-EINVAL); - } - - if (seq + asic_prop->max_pending_cs < ctx->cs_sequence) { - spin_unlock(&ctx->cs_lock); - return NULL; - } - - fence = dma_fence_get( - ctx->cs_pending[seq & (asic_prop->max_pending_cs - 1)]); - spin_unlock(&ctx->cs_lock); - - return fence; -} - -/* - * hl_ctx_mgr_init - initialize the context manager - * - * @mgr: pointer to context manager structure - * - * This manager is an object inside the hpriv object of the user process. - * The function is called when a user process opens the FD. - */ -void hl_ctx_mgr_init(struct hl_ctx_mgr *mgr) -{ - mutex_init(&mgr->ctx_lock); - idr_init(&mgr->ctx_handles); -} - -/* - * hl_ctx_mgr_fini - finalize the context manager - * - * @hdev: pointer to device structure - * @mgr: pointer to context manager structure - * - * This function goes over all the contexts in the manager and frees them. - * It is called when a process closes the FD. - */ -void hl_ctx_mgr_fini(struct hl_device *hdev, struct hl_ctx_mgr *mgr) -{ - struct hl_ctx *ctx; - struct idr *idp; - u32 id; - - idp = &mgr->ctx_handles; - - idr_for_each_entry(idp, ctx, id) - hl_ctx_free(hdev, ctx); - - idr_destroy(&mgr->ctx_handles); - mutex_destroy(&mgr->ctx_lock); -} diff --git a/drivers/misc/habanalabs/debugfs.c b/drivers/misc/habanalabs/debugfs.c deleted file mode 100644 index fc4372c18ce2..000000000000 --- a/drivers/misc/habanalabs/debugfs.c +++ /dev/null @@ -1,1411 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -/* - * Copyright 2016-2019 HabanaLabs, Ltd. - * All Rights Reserved. - */ - -#include "habanalabs.h" -#include "include/hw_ip/mmu/mmu_general.h" - -#include -#include -#include - -#define MMU_ADDR_BUF_SIZE 40 -#define MMU_ASID_BUF_SIZE 10 -#define MMU_KBUF_SIZE (MMU_ADDR_BUF_SIZE + MMU_ASID_BUF_SIZE) - -static struct dentry *hl_debug_root; - -static int hl_debugfs_i2c_read(struct hl_device *hdev, u8 i2c_bus, u8 i2c_addr, - u8 i2c_reg, u32 *val) -{ - struct armcp_packet pkt; - int rc; - - if (hl_device_disabled_or_in_reset(hdev)) - return -EBUSY; - - memset(&pkt, 0, sizeof(pkt)); - - pkt.ctl = cpu_to_le32(ARMCP_PACKET_I2C_RD << - ARMCP_PKT_CTL_OPCODE_SHIFT); - pkt.i2c_bus = i2c_bus; - pkt.i2c_addr = i2c_addr; - pkt.i2c_reg = i2c_reg; - - rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), - HL_DEVICE_TIMEOUT_USEC, (long *) val); - - if (rc) - dev_err(hdev->dev, "Failed to read from I2C, error %d\n", rc); - - return rc; -} - -static int hl_debugfs_i2c_write(struct hl_device *hdev, u8 i2c_bus, u8 i2c_addr, - u8 i2c_reg, u32 val) -{ - struct armcp_packet pkt; - int rc; - - if (hl_device_disabled_or_in_reset(hdev)) - return -EBUSY; - - memset(&pkt, 0, sizeof(pkt)); - - pkt.ctl = cpu_to_le32(ARMCP_PACKET_I2C_WR << - ARMCP_PKT_CTL_OPCODE_SHIFT); - pkt.i2c_bus = i2c_bus; - pkt.i2c_addr = i2c_addr; - pkt.i2c_reg = i2c_reg; - pkt.value = cpu_to_le64(val); - - rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), - HL_DEVICE_TIMEOUT_USEC, NULL); - - if (rc) - dev_err(hdev->dev, "Failed to write to I2C, error %d\n", rc); - - return rc; -} - -static void hl_debugfs_led_set(struct hl_device *hdev, u8 led, u8 state) -{ - struct armcp_packet pkt; - int rc; - - if (hl_device_disabled_or_in_reset(hdev)) - return; - - memset(&pkt, 0, sizeof(pkt)); - - pkt.ctl = cpu_to_le32(ARMCP_PACKET_LED_SET << - ARMCP_PKT_CTL_OPCODE_SHIFT); - pkt.led_index = cpu_to_le32(led); - pkt.value = cpu_to_le64(state); - - rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), - HL_DEVICE_TIMEOUT_USEC, NULL); - - if (rc) - dev_err(hdev->dev, "Failed to set LED %d, error %d\n", led, rc); -} - -static int command_buffers_show(struct seq_file *s, void *data) -{ - struct hl_debugfs_entry *entry = s->private; - struct hl_dbg_device_entry *dev_entry = entry->dev_entry; - struct hl_cb *cb; - bool first = true; - - spin_lock(&dev_entry->cb_spinlock); - - list_for_each_entry(cb, &dev_entry->cb_list, debugfs_list) { - if (first) { - first = false; - seq_puts(s, "\n"); - seq_puts(s, " CB ID CTX ID CB size CB RefCnt mmap? CS counter\n"); - seq_puts(s, "---------------------------------------------------------------\n"); - } - seq_printf(s, - " %03d %d 0x%08x %d %d %d\n", - cb->id, cb->ctx_id, cb->size, - kref_read(&cb->refcount), - cb->mmap, cb->cs_cnt); - } - - spin_unlock(&dev_entry->cb_spinlock); - - if (!first) - seq_puts(s, "\n"); - - return 0; -} - -static int command_submission_show(struct seq_file *s, void *data) -{ - struct hl_debugfs_entry *entry = s->private; - struct hl_dbg_device_entry *dev_entry = entry->dev_entry; - struct hl_cs *cs; - bool first = true; - - spin_lock(&dev_entry->cs_spinlock); - - list_for_each_entry(cs, &dev_entry->cs_list, debugfs_list) { - if (first) { - first = false; - seq_puts(s, "\n"); - seq_puts(s, " CS ID CTX ASID CS RefCnt Submitted Completed\n"); - seq_puts(s, "------------------------------------------------------\n"); - } - seq_printf(s, - " %llu %d %d %d %d\n", - cs->sequence, cs->ctx->asid, - kref_read(&cs->refcount), - cs->submitted, cs->completed); - } - - spin_unlock(&dev_entry->cs_spinlock); - - if (!first) - seq_puts(s, "\n"); - - return 0; -} - -static int command_submission_jobs_show(struct seq_file *s, void *data) -{ - struct hl_debugfs_entry *entry = s->private; - struct hl_dbg_device_entry *dev_entry = entry->dev_entry; - struct hl_cs_job *job; - bool first = true; - - spin_lock(&dev_entry->cs_job_spinlock); - - list_for_each_entry(job, &dev_entry->cs_job_list, debugfs_list) { - if (first) { - first = false; - seq_puts(s, "\n"); - seq_puts(s, " JOB ID CS ID CTX ASID H/W Queue\n"); - seq_puts(s, "---------------------------------------\n"); - } - if (job->cs) - seq_printf(s, - " %02d %llu %d %d\n", - job->id, job->cs->sequence, job->cs->ctx->asid, - job->hw_queue_id); - else - seq_printf(s, - " %02d 0 %d %d\n", - job->id, HL_KERNEL_ASID_ID, job->hw_queue_id); - } - - spin_unlock(&dev_entry->cs_job_spinlock); - - if (!first) - seq_puts(s, "\n"); - - return 0; -} - -static int userptr_show(struct seq_file *s, void *data) -{ - struct hl_debugfs_entry *entry = s->private; - struct hl_dbg_device_entry *dev_entry = entry->dev_entry; - struct hl_userptr *userptr; - char dma_dir[4][30] = {"DMA_BIDIRECTIONAL", "DMA_TO_DEVICE", - "DMA_FROM_DEVICE", "DMA_NONE"}; - bool first = true; - - spin_lock(&dev_entry->userptr_spinlock); - - list_for_each_entry(userptr, &dev_entry->userptr_list, debugfs_list) { - if (first) { - first = false; - seq_puts(s, "\n"); - seq_puts(s, " user virtual address size dma dir\n"); - seq_puts(s, "----------------------------------------------------------\n"); - } - seq_printf(s, - " 0x%-14llx %-10u %-30s\n", - userptr->addr, userptr->size, dma_dir[userptr->dir]); - } - - spin_unlock(&dev_entry->userptr_spinlock); - - if (!first) - seq_puts(s, "\n"); - - return 0; -} - -static int vm_show(struct seq_file *s, void *data) -{ - struct hl_debugfs_entry *entry = s->private; - struct hl_dbg_device_entry *dev_entry = entry->dev_entry; - struct hl_ctx *ctx; - struct hl_vm *vm; - struct hl_vm_hash_node *hnode; - struct hl_userptr *userptr; - struct hl_vm_phys_pg_pack *phys_pg_pack = NULL; - enum vm_type_t *vm_type; - bool once = true; - u64 j; - int i; - - if (!dev_entry->hdev->mmu_enable) - return 0; - - spin_lock(&dev_entry->ctx_mem_hash_spinlock); - - list_for_each_entry(ctx, &dev_entry->ctx_mem_hash_list, debugfs_list) { - once = false; - seq_puts(s, "\n\n----------------------------------------------------"); - seq_puts(s, "\n----------------------------------------------------\n\n"); - seq_printf(s, "ctx asid: %u\n", ctx->asid); - - seq_puts(s, "\nmappings:\n\n"); - seq_puts(s, " virtual address size handle\n"); - seq_puts(s, "----------------------------------------------------\n"); - mutex_lock(&ctx->mem_hash_lock); - hash_for_each(ctx->mem_hash, i, hnode, node) { - vm_type = hnode->ptr; - - if (*vm_type == VM_TYPE_USERPTR) { - userptr = hnode->ptr; - seq_printf(s, - " 0x%-14llx %-10u\n", - hnode->vaddr, userptr->size); - } else { - phys_pg_pack = hnode->ptr; - seq_printf(s, - " 0x%-14llx %-10llu %-4u\n", - hnode->vaddr, phys_pg_pack->total_size, - phys_pg_pack->handle); - } - } - mutex_unlock(&ctx->mem_hash_lock); - - vm = &ctx->hdev->vm; - spin_lock(&vm->idr_lock); - - if (!idr_is_empty(&vm->phys_pg_pack_handles)) - seq_puts(s, "\n\nallocations:\n"); - - idr_for_each_entry(&vm->phys_pg_pack_handles, phys_pg_pack, i) { - if (phys_pg_pack->asid != ctx->asid) - continue; - - seq_printf(s, "\nhandle: %u\n", phys_pg_pack->handle); - seq_printf(s, "page size: %u\n\n", - phys_pg_pack->page_size); - seq_puts(s, " physical address\n"); - seq_puts(s, "---------------------\n"); - for (j = 0 ; j < phys_pg_pack->npages ; j++) { - seq_printf(s, " 0x%-14llx\n", - phys_pg_pack->pages[j]); - } - } - spin_unlock(&vm->idr_lock); - - } - - spin_unlock(&dev_entry->ctx_mem_hash_spinlock); - - if (!once) - seq_puts(s, "\n"); - - return 0; -} - -/* these inline functions are copied from mmu.c */ -static inline u64 get_hop0_addr(struct hl_ctx *ctx) -{ - return ctx->hdev->asic_prop.mmu_pgt_addr + - (ctx->asid * ctx->hdev->asic_prop.mmu_hop_table_size); -} - -static inline u64 get_hopN_pte_addr(struct hl_ctx *ctx, u64 hop_addr, - u64 virt_addr, u64 mask, u64 shift) -{ - return hop_addr + ctx->hdev->asic_prop.mmu_pte_size * - ((virt_addr & mask) >> shift); -} - -static inline u64 get_hop0_pte_addr(struct hl_ctx *ctx, - struct hl_mmu_properties *mmu_specs, - u64 hop_addr, u64 vaddr) -{ - return get_hopN_pte_addr(ctx, hop_addr, vaddr, mmu_specs->hop0_mask, - mmu_specs->hop0_shift); -} - -static inline u64 get_hop1_pte_addr(struct hl_ctx *ctx, - struct hl_mmu_properties *mmu_specs, - u64 hop_addr, u64 vaddr) -{ - return get_hopN_pte_addr(ctx, hop_addr, vaddr, mmu_specs->hop1_mask, - mmu_specs->hop1_shift); -} - -static inline u64 get_hop2_pte_addr(struct hl_ctx *ctx, - struct hl_mmu_properties *mmu_specs, - u64 hop_addr, u64 vaddr) -{ - return get_hopN_pte_addr(ctx, hop_addr, vaddr, mmu_specs->hop2_mask, - mmu_specs->hop2_shift); -} - -static inline u64 get_hop3_pte_addr(struct hl_ctx *ctx, - struct hl_mmu_properties *mmu_specs, - u64 hop_addr, u64 vaddr) -{ - return get_hopN_pte_addr(ctx, hop_addr, vaddr, mmu_specs->hop3_mask, - mmu_specs->hop3_shift); -} - -static inline u64 get_hop4_pte_addr(struct hl_ctx *ctx, - struct hl_mmu_properties *mmu_specs, - u64 hop_addr, u64 vaddr) -{ - return get_hopN_pte_addr(ctx, hop_addr, vaddr, mmu_specs->hop4_mask, - mmu_specs->hop4_shift); -} - -static inline u64 get_next_hop_addr(u64 curr_pte) -{ - if (curr_pte & PAGE_PRESENT_MASK) - return curr_pte & HOP_PHYS_ADDR_MASK; - else - return ULLONG_MAX; -} - -static int mmu_show(struct seq_file *s, void *data) -{ - struct hl_debugfs_entry *entry = s->private; - struct hl_dbg_device_entry *dev_entry = entry->dev_entry; - struct hl_device *hdev = dev_entry->hdev; - struct asic_fixed_properties *prop = &hdev->asic_prop; - struct hl_mmu_properties *mmu_prop; - struct hl_ctx *ctx; - bool is_dram_addr; - - u64 hop0_addr = 0, hop0_pte_addr = 0, hop0_pte = 0, - hop1_addr = 0, hop1_pte_addr = 0, hop1_pte = 0, - hop2_addr = 0, hop2_pte_addr = 0, hop2_pte = 0, - hop3_addr = 0, hop3_pte_addr = 0, hop3_pte = 0, - hop4_addr = 0, hop4_pte_addr = 0, hop4_pte = 0, - virt_addr = dev_entry->mmu_addr; - - if (!hdev->mmu_enable) - return 0; - - if (dev_entry->mmu_asid == HL_KERNEL_ASID_ID) - ctx = hdev->kernel_ctx; - else - ctx = hdev->compute_ctx; - - if (!ctx) { - dev_err(hdev->dev, "no ctx available\n"); - return 0; - } - - is_dram_addr = hl_mem_area_inside_range(virt_addr, prop->dmmu.page_size, - prop->dmmu.start_addr, - prop->dmmu.end_addr); - - /* shifts and masks are the same in PMMU and HPMMU, use one of them */ - mmu_prop = is_dram_addr ? &prop->dmmu : &prop->pmmu; - - mutex_lock(&ctx->mmu_lock); - - /* the following lookup is copied from unmap() in mmu.c */ - - hop0_addr = get_hop0_addr(ctx); - hop0_pte_addr = get_hop0_pte_addr(ctx, mmu_prop, hop0_addr, virt_addr); - hop0_pte = hdev->asic_funcs->read_pte(hdev, hop0_pte_addr); - hop1_addr = get_next_hop_addr(hop0_pte); - - if (hop1_addr == ULLONG_MAX) - goto not_mapped; - - hop1_pte_addr = get_hop1_pte_addr(ctx, mmu_prop, hop1_addr, virt_addr); - hop1_pte = hdev->asic_funcs->read_pte(hdev, hop1_pte_addr); - hop2_addr = get_next_hop_addr(hop1_pte); - - if (hop2_addr == ULLONG_MAX) - goto not_mapped; - - hop2_pte_addr = get_hop2_pte_addr(ctx, mmu_prop, hop2_addr, virt_addr); - hop2_pte = hdev->asic_funcs->read_pte(hdev, hop2_pte_addr); - hop3_addr = get_next_hop_addr(hop2_pte); - - if (hop3_addr == ULLONG_MAX) - goto not_mapped; - - hop3_pte_addr = get_hop3_pte_addr(ctx, mmu_prop, hop3_addr, virt_addr); - hop3_pte = hdev->asic_funcs->read_pte(hdev, hop3_pte_addr); - - if (!(hop3_pte & LAST_MASK)) { - hop4_addr = get_next_hop_addr(hop3_pte); - - if (hop4_addr == ULLONG_MAX) - goto not_mapped; - - hop4_pte_addr = get_hop4_pte_addr(ctx, mmu_prop, hop4_addr, - virt_addr); - hop4_pte = hdev->asic_funcs->read_pte(hdev, hop4_pte_addr); - if (!(hop4_pte & PAGE_PRESENT_MASK)) - goto not_mapped; - } else { - if (!(hop3_pte & PAGE_PRESENT_MASK)) - goto not_mapped; - } - - seq_printf(s, "asid: %u, virt_addr: 0x%llx\n", - dev_entry->mmu_asid, dev_entry->mmu_addr); - - seq_printf(s, "hop0_addr: 0x%llx\n", hop0_addr); - seq_printf(s, "hop0_pte_addr: 0x%llx\n", hop0_pte_addr); - seq_printf(s, "hop0_pte: 0x%llx\n", hop0_pte); - - seq_printf(s, "hop1_addr: 0x%llx\n", hop1_addr); - seq_printf(s, "hop1_pte_addr: 0x%llx\n", hop1_pte_addr); - seq_printf(s, "hop1_pte: 0x%llx\n", hop1_pte); - - seq_printf(s, "hop2_addr: 0x%llx\n", hop2_addr); - seq_printf(s, "hop2_pte_addr: 0x%llx\n", hop2_pte_addr); - seq_printf(s, "hop2_pte: 0x%llx\n", hop2_pte); - - seq_printf(s, "hop3_addr: 0x%llx\n", hop3_addr); - seq_printf(s, "hop3_pte_addr: 0x%llx\n", hop3_pte_addr); - seq_printf(s, "hop3_pte: 0x%llx\n", hop3_pte); - - if (!(hop3_pte & LAST_MASK)) { - seq_printf(s, "hop4_addr: 0x%llx\n", hop4_addr); - seq_printf(s, "hop4_pte_addr: 0x%llx\n", hop4_pte_addr); - seq_printf(s, "hop4_pte: 0x%llx\n", hop4_pte); - } - - goto out; - -not_mapped: - dev_err(hdev->dev, "virt addr 0x%llx is not mapped to phys addr\n", - virt_addr); -out: - mutex_unlock(&ctx->mmu_lock); - - return 0; -} - -static ssize_t mmu_asid_va_write(struct file *file, const char __user *buf, - size_t count, loff_t *f_pos) -{ - struct seq_file *s = file->private_data; - struct hl_debugfs_entry *entry = s->private; - struct hl_dbg_device_entry *dev_entry = entry->dev_entry; - struct hl_device *hdev = dev_entry->hdev; - char kbuf[MMU_KBUF_SIZE]; - char *c; - ssize_t rc; - - if (!hdev->mmu_enable) - return count; - - if (count > sizeof(kbuf) - 1) - goto err; - if (copy_from_user(kbuf, buf, count)) - goto err; - kbuf[count] = 0; - - c = strchr(kbuf, ' '); - if (!c) - goto err; - *c = '\0'; - - rc = kstrtouint(kbuf, 10, &dev_entry->mmu_asid); - if (rc) - goto err; - - if (strncmp(c+1, "0x", 2)) - goto err; - rc = kstrtoull(c+3, 16, &dev_entry->mmu_addr); - if (rc) - goto err; - - return count; - -err: - dev_err(hdev->dev, "usage: echo <0xaddr> > mmu\n"); - - return -EINVAL; -} - -static int engines_show(struct seq_file *s, void *data) -{ - struct hl_debugfs_entry *entry = s->private; - struct hl_dbg_device_entry *dev_entry = entry->dev_entry; - struct hl_device *hdev = dev_entry->hdev; - - if (atomic_read(&hdev->in_reset)) { - dev_warn_ratelimited(hdev->dev, - "Can't check device idle during reset\n"); - return 0; - } - - hdev->asic_funcs->is_device_idle(hdev, NULL, s); - - return 0; -} - -static bool hl_is_device_va(struct hl_device *hdev, u64 addr) -{ - struct asic_fixed_properties *prop = &hdev->asic_prop; - - if (!hdev->mmu_enable) - goto out; - - if (hdev->dram_supports_virtual_memory && - (addr >= prop->dmmu.start_addr && addr < prop->dmmu.end_addr)) - return true; - - if (addr >= prop->pmmu.start_addr && - addr < prop->pmmu.end_addr) - return true; - - if (addr >= prop->pmmu_huge.start_addr && - addr < prop->pmmu_huge.end_addr) - return true; -out: - return false; -} - -static int device_va_to_pa(struct hl_device *hdev, u64 virt_addr, - u64 *phys_addr) -{ - struct hl_ctx *ctx = hdev->compute_ctx; - struct asic_fixed_properties *prop = &hdev->asic_prop; - struct hl_mmu_properties *mmu_prop; - u64 hop_addr, hop_pte_addr, hop_pte; - u64 offset_mask = HOP4_MASK | FLAGS_MASK; - int rc = 0; - bool is_dram_addr; - - if (!ctx) { - dev_err(hdev->dev, "no ctx available\n"); - return -EINVAL; - } - - is_dram_addr = hl_mem_area_inside_range(virt_addr, prop->dmmu.page_size, - prop->dmmu.start_addr, - prop->dmmu.end_addr); - - /* shifts and masks are the same in PMMU and HPMMU, use one of them */ - mmu_prop = is_dram_addr ? &prop->dmmu : &prop->pmmu; - - mutex_lock(&ctx->mmu_lock); - - /* hop 0 */ - hop_addr = get_hop0_addr(ctx); - hop_pte_addr = get_hop0_pte_addr(ctx, mmu_prop, hop_addr, virt_addr); - hop_pte = hdev->asic_funcs->read_pte(hdev, hop_pte_addr); - - /* hop 1 */ - hop_addr = get_next_hop_addr(hop_pte); - if (hop_addr == ULLONG_MAX) - goto not_mapped; - hop_pte_addr = get_hop1_pte_addr(ctx, mmu_prop, hop_addr, virt_addr); - hop_pte = hdev->asic_funcs->read_pte(hdev, hop_pte_addr); - - /* hop 2 */ - hop_addr = get_next_hop_addr(hop_pte); - if (hop_addr == ULLONG_MAX) - goto not_mapped; - hop_pte_addr = get_hop2_pte_addr(ctx, mmu_prop, hop_addr, virt_addr); - hop_pte = hdev->asic_funcs->read_pte(hdev, hop_pte_addr); - - /* hop 3 */ - hop_addr = get_next_hop_addr(hop_pte); - if (hop_addr == ULLONG_MAX) - goto not_mapped; - hop_pte_addr = get_hop3_pte_addr(ctx, mmu_prop, hop_addr, virt_addr); - hop_pte = hdev->asic_funcs->read_pte(hdev, hop_pte_addr); - - if (!(hop_pte & LAST_MASK)) { - /* hop 4 */ - hop_addr = get_next_hop_addr(hop_pte); - if (hop_addr == ULLONG_MAX) - goto not_mapped; - hop_pte_addr = get_hop4_pte_addr(ctx, mmu_prop, hop_addr, - virt_addr); - hop_pte = hdev->asic_funcs->read_pte(hdev, hop_pte_addr); - - offset_mask = FLAGS_MASK; - } - - if (!(hop_pte & PAGE_PRESENT_MASK)) - goto not_mapped; - - *phys_addr = (hop_pte & ~offset_mask) | (virt_addr & offset_mask); - - goto out; - -not_mapped: - dev_err(hdev->dev, "virt addr 0x%llx is not mapped to phys addr\n", - virt_addr); - rc = -EINVAL; -out: - mutex_unlock(&ctx->mmu_lock); - return rc; -} - -static ssize_t hl_data_read32(struct file *f, char __user *buf, - size_t count, loff_t *ppos) -{ - struct hl_dbg_device_entry *entry = file_inode(f)->i_private; - struct hl_device *hdev = entry->hdev; - char tmp_buf[32]; - u64 addr = entry->addr; - u32 val; - ssize_t rc; - - if (atomic_read(&hdev->in_reset)) { - dev_warn_ratelimited(hdev->dev, "Can't read during reset\n"); - return 0; - } - - if (*ppos) - return 0; - - if (hl_is_device_va(hdev, addr)) { - rc = device_va_to_pa(hdev, addr, &addr); - if (rc) - return rc; - } - - rc = hdev->asic_funcs->debugfs_read32(hdev, addr, &val); - if (rc) { - dev_err(hdev->dev, "Failed to read from 0x%010llx\n", addr); - return rc; - } - - sprintf(tmp_buf, "0x%08x\n", val); - return simple_read_from_buffer(buf, count, ppos, tmp_buf, - strlen(tmp_buf)); -} - -static ssize_t hl_data_write32(struct file *f, const char __user *buf, - size_t count, loff_t *ppos) -{ - struct hl_dbg_device_entry *entry = file_inode(f)->i_private; - struct hl_device *hdev = entry->hdev; - u64 addr = entry->addr; - u32 value; - ssize_t rc; - - if (atomic_read(&hdev->in_reset)) { - dev_warn_ratelimited(hdev->dev, "Can't write during reset\n"); - return 0; - } - - rc = kstrtouint_from_user(buf, count, 16, &value); - if (rc) - return rc; - - if (hl_is_device_va(hdev, addr)) { - rc = device_va_to_pa(hdev, addr, &addr); - if (rc) - return rc; - } - - rc = hdev->asic_funcs->debugfs_write32(hdev, addr, value); - if (rc) { - dev_err(hdev->dev, "Failed to write 0x%08x to 0x%010llx\n", - value, addr); - return rc; - } - - return count; -} - -static ssize_t hl_data_read64(struct file *f, char __user *buf, - size_t count, loff_t *ppos) -{ - struct hl_dbg_device_entry *entry = file_inode(f)->i_private; - struct hl_device *hdev = entry->hdev; - char tmp_buf[32]; - u64 addr = entry->addr; - u64 val; - ssize_t rc; - - if (*ppos) - return 0; - - if (hl_is_device_va(hdev, addr)) { - rc = device_va_to_pa(hdev, addr, &addr); - if (rc) - return rc; - } - - rc = hdev->asic_funcs->debugfs_read64(hdev, addr, &val); - if (rc) { - dev_err(hdev->dev, "Failed to read from 0x%010llx\n", addr); - return rc; - } - - sprintf(tmp_buf, "0x%016llx\n", val); - return simple_read_from_buffer(buf, count, ppos, tmp_buf, - strlen(tmp_buf)); -} - -static ssize_t hl_data_write64(struct file *f, const char __user *buf, - size_t count, loff_t *ppos) -{ - struct hl_dbg_device_entry *entry = file_inode(f)->i_private; - struct hl_device *hdev = entry->hdev; - u64 addr = entry->addr; - u64 value; - ssize_t rc; - - rc = kstrtoull_from_user(buf, count, 16, &value); - if (rc) - return rc; - - if (hl_is_device_va(hdev, addr)) { - rc = device_va_to_pa(hdev, addr, &addr); - if (rc) - return rc; - } - - rc = hdev->asic_funcs->debugfs_write64(hdev, addr, value); - if (rc) { - dev_err(hdev->dev, "Failed to write 0x%016llx to 0x%010llx\n", - value, addr); - return rc; - } - - return count; -} - -static ssize_t hl_get_power_state(struct file *f, char __user *buf, - size_t count, loff_t *ppos) -{ - struct hl_dbg_device_entry *entry = file_inode(f)->i_private; - struct hl_device *hdev = entry->hdev; - char tmp_buf[200]; - int i; - - if (*ppos) - return 0; - - if (hdev->pdev->current_state == PCI_D0) - i = 1; - else if (hdev->pdev->current_state == PCI_D3hot) - i = 2; - else - i = 3; - - sprintf(tmp_buf, - "current power state: %d\n1 - D0\n2 - D3hot\n3 - Unknown\n", i); - return simple_read_from_buffer(buf, count, ppos, tmp_buf, - strlen(tmp_buf)); -} - -static ssize_t hl_set_power_state(struct file *f, const char __user *buf, - size_t count, loff_t *ppos) -{ - struct hl_dbg_device_entry *entry = file_inode(f)->i_private; - struct hl_device *hdev = entry->hdev; - u32 value; - ssize_t rc; - - rc = kstrtouint_from_user(buf, count, 10, &value); - if (rc) - return rc; - - if (value == 1) { - pci_set_power_state(hdev->pdev, PCI_D0); - pci_restore_state(hdev->pdev); - rc = pci_enable_device(hdev->pdev); - } else if (value == 2) { - pci_save_state(hdev->pdev); - pci_disable_device(hdev->pdev); - pci_set_power_state(hdev->pdev, PCI_D3hot); - } else { - dev_dbg(hdev->dev, "invalid power state value %u\n", value); - return -EINVAL; - } - - return count; -} - -static ssize_t hl_i2c_data_read(struct file *f, char __user *buf, - size_t count, loff_t *ppos) -{ - struct hl_dbg_device_entry *entry = file_inode(f)->i_private; - struct hl_device *hdev = entry->hdev; - char tmp_buf[32]; - u32 val; - ssize_t rc; - - if (*ppos) - return 0; - - rc = hl_debugfs_i2c_read(hdev, entry->i2c_bus, entry->i2c_addr, - entry->i2c_reg, &val); - if (rc) { - dev_err(hdev->dev, - "Failed to read from I2C bus %d, addr %d, reg %d\n", - entry->i2c_bus, entry->i2c_addr, entry->i2c_reg); - return rc; - } - - sprintf(tmp_buf, "0x%02x\n", val); - rc = simple_read_from_buffer(buf, count, ppos, tmp_buf, - strlen(tmp_buf)); - - return rc; -} - -static ssize_t hl_i2c_data_write(struct file *f, const char __user *buf, - size_t count, loff_t *ppos) -{ - struct hl_dbg_device_entry *entry = file_inode(f)->i_private; - struct hl_device *hdev = entry->hdev; - u32 value; - ssize_t rc; - - rc = kstrtouint_from_user(buf, count, 16, &value); - if (rc) - return rc; - - rc = hl_debugfs_i2c_write(hdev, entry->i2c_bus, entry->i2c_addr, - entry->i2c_reg, value); - if (rc) { - dev_err(hdev->dev, - "Failed to write 0x%02x to I2C bus %d, addr %d, reg %d\n", - value, entry->i2c_bus, entry->i2c_addr, entry->i2c_reg); - return rc; - } - - return count; -} - -static ssize_t hl_led0_write(struct file *f, const char __user *buf, - size_t count, loff_t *ppos) -{ - struct hl_dbg_device_entry *entry = file_inode(f)->i_private; - struct hl_device *hdev = entry->hdev; - u32 value; - ssize_t rc; - - rc = kstrtouint_from_user(buf, count, 10, &value); - if (rc) - return rc; - - value = value ? 1 : 0; - - hl_debugfs_led_set(hdev, 0, value); - - return count; -} - -static ssize_t hl_led1_write(struct file *f, const char __user *buf, - size_t count, loff_t *ppos) -{ - struct hl_dbg_device_entry *entry = file_inode(f)->i_private; - struct hl_device *hdev = entry->hdev; - u32 value; - ssize_t rc; - - rc = kstrtouint_from_user(buf, count, 10, &value); - if (rc) - return rc; - - value = value ? 1 : 0; - - hl_debugfs_led_set(hdev, 1, value); - - return count; -} - -static ssize_t hl_led2_write(struct file *f, const char __user *buf, - size_t count, loff_t *ppos) -{ - struct hl_dbg_device_entry *entry = file_inode(f)->i_private; - struct hl_device *hdev = entry->hdev; - u32 value; - ssize_t rc; - - rc = kstrtouint_from_user(buf, count, 10, &value); - if (rc) - return rc; - - value = value ? 1 : 0; - - hl_debugfs_led_set(hdev, 2, value); - - return count; -} - -static ssize_t hl_device_read(struct file *f, char __user *buf, - size_t count, loff_t *ppos) -{ - static const char *help = - "Valid values: disable, enable, suspend, resume, cpu_timeout\n"; - return simple_read_from_buffer(buf, count, ppos, help, strlen(help)); -} - -static ssize_t hl_device_write(struct file *f, const char __user *buf, - size_t count, loff_t *ppos) -{ - struct hl_dbg_device_entry *entry = file_inode(f)->i_private; - struct hl_device *hdev = entry->hdev; - char data[30] = {0}; - - /* don't allow partial writes */ - if (*ppos != 0) - return 0; - - simple_write_to_buffer(data, 29, ppos, buf, count); - - if (strncmp("disable", data, strlen("disable")) == 0) { - hdev->disabled = true; - } else if (strncmp("enable", data, strlen("enable")) == 0) { - hdev->disabled = false; - } else if (strncmp("suspend", data, strlen("suspend")) == 0) { - hdev->asic_funcs->suspend(hdev); - } else if (strncmp("resume", data, strlen("resume")) == 0) { - hdev->asic_funcs->resume(hdev); - } else if (strncmp("cpu_timeout", data, strlen("cpu_timeout")) == 0) { - hdev->device_cpu_disabled = true; - } else { - dev_err(hdev->dev, - "Valid values: disable, enable, suspend, resume, cpu_timeout\n"); - count = -EINVAL; - } - - return count; -} - -static ssize_t hl_clk_gate_read(struct file *f, char __user *buf, - size_t count, loff_t *ppos) -{ - struct hl_dbg_device_entry *entry = file_inode(f)->i_private; - struct hl_device *hdev = entry->hdev; - char tmp_buf[200]; - ssize_t rc; - - if (*ppos) - return 0; - - sprintf(tmp_buf, "%d\n", hdev->clock_gating); - rc = simple_read_from_buffer(buf, strlen(tmp_buf) + 1, ppos, tmp_buf, - strlen(tmp_buf) + 1); - - return rc; -} - -static ssize_t hl_clk_gate_write(struct file *f, const char __user *buf, - size_t count, loff_t *ppos) -{ - struct hl_dbg_device_entry *entry = file_inode(f)->i_private; - struct hl_device *hdev = entry->hdev; - u32 value; - ssize_t rc; - - if (atomic_read(&hdev->in_reset)) { - dev_warn_ratelimited(hdev->dev, - "Can't change clock gating during reset\n"); - return 0; - } - - rc = kstrtouint_from_user(buf, count, 10, &value); - if (rc) - return rc; - - if (value) { - hdev->clock_gating = 1; - if (hdev->asic_funcs->enable_clock_gating) - hdev->asic_funcs->enable_clock_gating(hdev); - } else { - if (hdev->asic_funcs->disable_clock_gating) - hdev->asic_funcs->disable_clock_gating(hdev); - hdev->clock_gating = 0; - } - - return count; -} - -static ssize_t hl_stop_on_err_read(struct file *f, char __user *buf, - size_t count, loff_t *ppos) -{ - struct hl_dbg_device_entry *entry = file_inode(f)->i_private; - struct hl_device *hdev = entry->hdev; - char tmp_buf[200]; - ssize_t rc; - - if (*ppos) - return 0; - - sprintf(tmp_buf, "%d\n", hdev->stop_on_err); - rc = simple_read_from_buffer(buf, strlen(tmp_buf) + 1, ppos, tmp_buf, - strlen(tmp_buf) + 1); - - return rc; -} - -static ssize_t hl_stop_on_err_write(struct file *f, const char __user *buf, - size_t count, loff_t *ppos) -{ - struct hl_dbg_device_entry *entry = file_inode(f)->i_private; - struct hl_device *hdev = entry->hdev; - u32 value; - ssize_t rc; - - if (atomic_read(&hdev->in_reset)) { - dev_warn_ratelimited(hdev->dev, - "Can't change stop on error during reset\n"); - return 0; - } - - rc = kstrtouint_from_user(buf, count, 10, &value); - if (rc) - return rc; - - hdev->stop_on_err = value ? 1 : 0; - - hl_device_reset(hdev, false, false); - - return count; -} - -static const struct file_operations hl_data32b_fops = { - .owner = THIS_MODULE, - .read = hl_data_read32, - .write = hl_data_write32 -}; - -static const struct file_operations hl_data64b_fops = { - .owner = THIS_MODULE, - .read = hl_data_read64, - .write = hl_data_write64 -}; - -static const struct file_operations hl_i2c_data_fops = { - .owner = THIS_MODULE, - .read = hl_i2c_data_read, - .write = hl_i2c_data_write -}; - -static const struct file_operations hl_power_fops = { - .owner = THIS_MODULE, - .read = hl_get_power_state, - .write = hl_set_power_state -}; - -static const struct file_operations hl_led0_fops = { - .owner = THIS_MODULE, - .write = hl_led0_write -}; - -static const struct file_operations hl_led1_fops = { - .owner = THIS_MODULE, - .write = hl_led1_write -}; - -static const struct file_operations hl_led2_fops = { - .owner = THIS_MODULE, - .write = hl_led2_write -}; - -static const struct file_operations hl_device_fops = { - .owner = THIS_MODULE, - .read = hl_device_read, - .write = hl_device_write -}; - -static const struct file_operations hl_clk_gate_fops = { - .owner = THIS_MODULE, - .read = hl_clk_gate_read, - .write = hl_clk_gate_write -}; - -static const struct file_operations hl_stop_on_err_fops = { - .owner = THIS_MODULE, - .read = hl_stop_on_err_read, - .write = hl_stop_on_err_write -}; - -static const struct hl_info_list hl_debugfs_list[] = { - {"command_buffers", command_buffers_show, NULL}, - {"command_submission", command_submission_show, NULL}, - {"command_submission_jobs", command_submission_jobs_show, NULL}, - {"userptr", userptr_show, NULL}, - {"vm", vm_show, NULL}, - {"mmu", mmu_show, mmu_asid_va_write}, - {"engines", engines_show, NULL} -}; - -static int hl_debugfs_open(struct inode *inode, struct file *file) -{ - struct hl_debugfs_entry *node = inode->i_private; - - return single_open(file, node->info_ent->show, node); -} - -static ssize_t hl_debugfs_write(struct file *file, const char __user *buf, - size_t count, loff_t *f_pos) -{ - struct hl_debugfs_entry *node = file->f_inode->i_private; - - if (node->info_ent->write) - return node->info_ent->write(file, buf, count, f_pos); - else - return -EINVAL; - -} - -static const struct file_operations hl_debugfs_fops = { - .owner = THIS_MODULE, - .open = hl_debugfs_open, - .read = seq_read, - .write = hl_debugfs_write, - .llseek = seq_lseek, - .release = single_release, -}; - -void hl_debugfs_add_device(struct hl_device *hdev) -{ - struct hl_dbg_device_entry *dev_entry = &hdev->hl_debugfs; - int count = ARRAY_SIZE(hl_debugfs_list); - struct hl_debugfs_entry *entry; - struct dentry *ent; - int i; - - dev_entry->hdev = hdev; - dev_entry->entry_arr = kmalloc_array(count, - sizeof(struct hl_debugfs_entry), - GFP_KERNEL); - if (!dev_entry->entry_arr) - return; - - INIT_LIST_HEAD(&dev_entry->file_list); - INIT_LIST_HEAD(&dev_entry->cb_list); - INIT_LIST_HEAD(&dev_entry->cs_list); - INIT_LIST_HEAD(&dev_entry->cs_job_list); - INIT_LIST_HEAD(&dev_entry->userptr_list); - INIT_LIST_HEAD(&dev_entry->ctx_mem_hash_list); - mutex_init(&dev_entry->file_mutex); - spin_lock_init(&dev_entry->cb_spinlock); - spin_lock_init(&dev_entry->cs_spinlock); - spin_lock_init(&dev_entry->cs_job_spinlock); - spin_lock_init(&dev_entry->userptr_spinlock); - spin_lock_init(&dev_entry->ctx_mem_hash_spinlock); - - dev_entry->root = debugfs_create_dir(dev_name(hdev->dev), - hl_debug_root); - - debugfs_create_x64("addr", - 0644, - dev_entry->root, - &dev_entry->addr); - - debugfs_create_file("data32", - 0644, - dev_entry->root, - dev_entry, - &hl_data32b_fops); - - debugfs_create_file("data64", - 0644, - dev_entry->root, - dev_entry, - &hl_data64b_fops); - - debugfs_create_file("set_power_state", - 0200, - dev_entry->root, - dev_entry, - &hl_power_fops); - - debugfs_create_u8("i2c_bus", - 0644, - dev_entry->root, - &dev_entry->i2c_bus); - - debugfs_create_u8("i2c_addr", - 0644, - dev_entry->root, - &dev_entry->i2c_addr); - - debugfs_create_u8("i2c_reg", - 0644, - dev_entry->root, - &dev_entry->i2c_reg); - - debugfs_create_file("i2c_data", - 0644, - dev_entry->root, - dev_entry, - &hl_i2c_data_fops); - - debugfs_create_file("led0", - 0200, - dev_entry->root, - dev_entry, - &hl_led0_fops); - - debugfs_create_file("led1", - 0200, - dev_entry->root, - dev_entry, - &hl_led1_fops); - - debugfs_create_file("led2", - 0200, - dev_entry->root, - dev_entry, - &hl_led2_fops); - - debugfs_create_file("device", - 0200, - dev_entry->root, - dev_entry, - &hl_device_fops); - - debugfs_create_file("clk_gate", - 0200, - dev_entry->root, - dev_entry, - &hl_clk_gate_fops); - - debugfs_create_file("stop_on_err", - 0644, - dev_entry->root, - dev_entry, - &hl_stop_on_err_fops); - - for (i = 0, entry = dev_entry->entry_arr ; i < count ; i++, entry++) { - - ent = debugfs_create_file(hl_debugfs_list[i].name, - 0444, - dev_entry->root, - entry, - &hl_debugfs_fops); - entry->dent = ent; - entry->info_ent = &hl_debugfs_list[i]; - entry->dev_entry = dev_entry; - } -} - -void hl_debugfs_remove_device(struct hl_device *hdev) -{ - struct hl_dbg_device_entry *entry = &hdev->hl_debugfs; - - debugfs_remove_recursive(entry->root); - - mutex_destroy(&entry->file_mutex); - kfree(entry->entry_arr); -} - -void hl_debugfs_add_file(struct hl_fpriv *hpriv) -{ - struct hl_dbg_device_entry *dev_entry = &hpriv->hdev->hl_debugfs; - - mutex_lock(&dev_entry->file_mutex); - list_add(&hpriv->debugfs_list, &dev_entry->file_list); - mutex_unlock(&dev_entry->file_mutex); -} - -void hl_debugfs_remove_file(struct hl_fpriv *hpriv) -{ - struct hl_dbg_device_entry *dev_entry = &hpriv->hdev->hl_debugfs; - - mutex_lock(&dev_entry->file_mutex); - list_del(&hpriv->debugfs_list); - mutex_unlock(&dev_entry->file_mutex); -} - -void hl_debugfs_add_cb(struct hl_cb *cb) -{ - struct hl_dbg_device_entry *dev_entry = &cb->hdev->hl_debugfs; - - spin_lock(&dev_entry->cb_spinlock); - list_add(&cb->debugfs_list, &dev_entry->cb_list); - spin_unlock(&dev_entry->cb_spinlock); -} - -void hl_debugfs_remove_cb(struct hl_cb *cb) -{ - struct hl_dbg_device_entry *dev_entry = &cb->hdev->hl_debugfs; - - spin_lock(&dev_entry->cb_spinlock); - list_del(&cb->debugfs_list); - spin_unlock(&dev_entry->cb_spinlock); -} - -void hl_debugfs_add_cs(struct hl_cs *cs) -{ - struct hl_dbg_device_entry *dev_entry = &cs->ctx->hdev->hl_debugfs; - - spin_lock(&dev_entry->cs_spinlock); - list_add(&cs->debugfs_list, &dev_entry->cs_list); - spin_unlock(&dev_entry->cs_spinlock); -} - -void hl_debugfs_remove_cs(struct hl_cs *cs) -{ - struct hl_dbg_device_entry *dev_entry = &cs->ctx->hdev->hl_debugfs; - - spin_lock(&dev_entry->cs_spinlock); - list_del(&cs->debugfs_list); - spin_unlock(&dev_entry->cs_spinlock); -} - -void hl_debugfs_add_job(struct hl_device *hdev, struct hl_cs_job *job) -{ - struct hl_dbg_device_entry *dev_entry = &hdev->hl_debugfs; - - spin_lock(&dev_entry->cs_job_spinlock); - list_add(&job->debugfs_list, &dev_entry->cs_job_list); - spin_unlock(&dev_entry->cs_job_spinlock); -} - -void hl_debugfs_remove_job(struct hl_device *hdev, struct hl_cs_job *job) -{ - struct hl_dbg_device_entry *dev_entry = &hdev->hl_debugfs; - - spin_lock(&dev_entry->cs_job_spinlock); - list_del(&job->debugfs_list); - spin_unlock(&dev_entry->cs_job_spinlock); -} - -void hl_debugfs_add_userptr(struct hl_device *hdev, struct hl_userptr *userptr) -{ - struct hl_dbg_device_entry *dev_entry = &hdev->hl_debugfs; - - spin_lock(&dev_entry->userptr_spinlock); - list_add(&userptr->debugfs_list, &dev_entry->userptr_list); - spin_unlock(&dev_entry->userptr_spinlock); -} - -void hl_debugfs_remove_userptr(struct hl_device *hdev, - struct hl_userptr *userptr) -{ - struct hl_dbg_device_entry *dev_entry = &hdev->hl_debugfs; - - spin_lock(&dev_entry->userptr_spinlock); - list_del(&userptr->debugfs_list); - spin_unlock(&dev_entry->userptr_spinlock); -} - -void hl_debugfs_add_ctx_mem_hash(struct hl_device *hdev, struct hl_ctx *ctx) -{ - struct hl_dbg_device_entry *dev_entry = &hdev->hl_debugfs; - - spin_lock(&dev_entry->ctx_mem_hash_spinlock); - list_add(&ctx->debugfs_list, &dev_entry->ctx_mem_hash_list); - spin_unlock(&dev_entry->ctx_mem_hash_spinlock); -} - -void hl_debugfs_remove_ctx_mem_hash(struct hl_device *hdev, struct hl_ctx *ctx) -{ - struct hl_dbg_device_entry *dev_entry = &hdev->hl_debugfs; - - spin_lock(&dev_entry->ctx_mem_hash_spinlock); - list_del(&ctx->debugfs_list); - spin_unlock(&dev_entry->ctx_mem_hash_spinlock); -} - -void __init hl_debugfs_init(void) -{ - hl_debug_root = debugfs_create_dir("habanalabs", NULL); -} - -void hl_debugfs_fini(void) -{ - debugfs_remove_recursive(hl_debug_root); -} diff --git a/drivers/misc/habanalabs/device.c b/drivers/misc/habanalabs/device.c deleted file mode 100644 index 84800efec10d..000000000000 --- a/drivers/misc/habanalabs/device.c +++ /dev/null @@ -1,1506 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -/* - * Copyright 2016-2019 HabanaLabs, Ltd. - * All Rights Reserved. - */ - -#define pr_fmt(fmt) "habanalabs: " fmt - -#include "habanalabs.h" - -#include -#include -#include -#include - -#define HL_PLDM_PENDING_RESET_PER_SEC (HL_PENDING_RESET_PER_SEC * 10) - -bool hl_device_disabled_or_in_reset(struct hl_device *hdev) -{ - if ((hdev->disabled) || (atomic_read(&hdev->in_reset))) - return true; - else - return false; -} - -enum hl_device_status hl_device_status(struct hl_device *hdev) -{ - enum hl_device_status status; - - if (hdev->disabled) - status = HL_DEVICE_STATUS_MALFUNCTION; - else if (atomic_read(&hdev->in_reset)) - status = HL_DEVICE_STATUS_IN_RESET; - else - status = HL_DEVICE_STATUS_OPERATIONAL; - - return status; -} - -static void hpriv_release(struct kref *ref) -{ - struct hl_fpriv *hpriv; - struct hl_device *hdev; - - hpriv = container_of(ref, struct hl_fpriv, refcount); - - hdev = hpriv->hdev; - - put_pid(hpriv->taskpid); - - hl_debugfs_remove_file(hpriv); - - mutex_destroy(&hpriv->restore_phase_mutex); - - mutex_lock(&hdev->fpriv_list_lock); - list_del(&hpriv->dev_node); - hdev->compute_ctx = NULL; - mutex_unlock(&hdev->fpriv_list_lock); - - kfree(hpriv); -} - -void hl_hpriv_get(struct hl_fpriv *hpriv) -{ - kref_get(&hpriv->refcount); -} - -void hl_hpriv_put(struct hl_fpriv *hpriv) -{ - kref_put(&hpriv->refcount, hpriv_release); -} - -/* - * hl_device_release - release function for habanalabs device - * - * @inode: pointer to inode structure - * @filp: pointer to file structure - * - * Called when process closes an habanalabs device - */ -static int hl_device_release(struct inode *inode, struct file *filp) -{ - struct hl_fpriv *hpriv = filp->private_data; - - hl_cb_mgr_fini(hpriv->hdev, &hpriv->cb_mgr); - hl_ctx_mgr_fini(hpriv->hdev, &hpriv->ctx_mgr); - - filp->private_data = NULL; - - hl_hpriv_put(hpriv); - - return 0; -} - -static int hl_device_release_ctrl(struct inode *inode, struct file *filp) -{ - struct hl_fpriv *hpriv = filp->private_data; - struct hl_device *hdev; - - filp->private_data = NULL; - - hdev = hpriv->hdev; - - mutex_lock(&hdev->fpriv_list_lock); - list_del(&hpriv->dev_node); - mutex_unlock(&hdev->fpriv_list_lock); - - kfree(hpriv); - - return 0; -} - -/* - * hl_mmap - mmap function for habanalabs device - * - * @*filp: pointer to file structure - * @*vma: pointer to vm_area_struct of the process - * - * Called when process does an mmap on habanalabs device. Call the device's mmap - * function at the end of the common code. - */ -static int hl_mmap(struct file *filp, struct vm_area_struct *vma) -{ - struct hl_fpriv *hpriv = filp->private_data; - - if ((vma->vm_pgoff & HL_MMAP_CB_MASK) == HL_MMAP_CB_MASK) { - vma->vm_pgoff ^= HL_MMAP_CB_MASK; - return hl_cb_mmap(hpriv, vma); - } - - return -EINVAL; -} - -static const struct file_operations hl_ops = { - .owner = THIS_MODULE, - .open = hl_device_open, - .release = hl_device_release, - .mmap = hl_mmap, - .unlocked_ioctl = hl_ioctl, - .compat_ioctl = hl_ioctl -}; - -static const struct file_operations hl_ctrl_ops = { - .owner = THIS_MODULE, - .open = hl_device_open_ctrl, - .release = hl_device_release_ctrl, - .unlocked_ioctl = hl_ioctl_control, - .compat_ioctl = hl_ioctl_control -}; - -static void device_release_func(struct device *dev) -{ - kfree(dev); -} - -/* - * device_init_cdev - Initialize cdev and device for habanalabs device - * - * @hdev: pointer to habanalabs device structure - * @hclass: pointer to the class object of the device - * @minor: minor number of the specific device - * @fpos: file operations to install for this device - * @name: name of the device as it will appear in the filesystem - * @cdev: pointer to the char device object that will be initialized - * @dev: pointer to the device object that will be initialized - * - * Initialize a cdev and a Linux device for habanalabs's device. - */ -static int device_init_cdev(struct hl_device *hdev, struct class *hclass, - int minor, const struct file_operations *fops, - char *name, struct cdev *cdev, - struct device **dev) -{ - cdev_init(cdev, fops); - cdev->owner = THIS_MODULE; - - *dev = kzalloc(sizeof(**dev), GFP_KERNEL); - if (!*dev) - return -ENOMEM; - - device_initialize(*dev); - (*dev)->devt = MKDEV(hdev->major, minor); - (*dev)->class = hclass; - (*dev)->release = device_release_func; - dev_set_drvdata(*dev, hdev); - dev_set_name(*dev, "%s", name); - - return 0; -} - -static int device_cdev_sysfs_add(struct hl_device *hdev) -{ - int rc; - - rc = cdev_device_add(&hdev->cdev, hdev->dev); - if (rc) { - dev_err(hdev->dev, - "failed to add a char device to the system\n"); - return rc; - } - - rc = cdev_device_add(&hdev->cdev_ctrl, hdev->dev_ctrl); - if (rc) { - dev_err(hdev->dev, - "failed to add a control char device to the system\n"); - goto delete_cdev_device; - } - - /* hl_sysfs_init() must be done after adding the device to the system */ - rc = hl_sysfs_init(hdev); - if (rc) { - dev_err(hdev->dev, "failed to initialize sysfs\n"); - goto delete_ctrl_cdev_device; - } - - hdev->cdev_sysfs_created = true; - - return 0; - -delete_ctrl_cdev_device: - cdev_device_del(&hdev->cdev_ctrl, hdev->dev_ctrl); -delete_cdev_device: - cdev_device_del(&hdev->cdev, hdev->dev); - return rc; -} - -static void device_cdev_sysfs_del(struct hl_device *hdev) -{ - /* device_release() won't be called so must free devices explicitly */ - if (!hdev->cdev_sysfs_created) { - kfree(hdev->dev_ctrl); - kfree(hdev->dev); - return; - } - - hl_sysfs_fini(hdev); - cdev_device_del(&hdev->cdev_ctrl, hdev->dev_ctrl); - cdev_device_del(&hdev->cdev, hdev->dev); -} - -/* - * device_early_init - do some early initialization for the habanalabs device - * - * @hdev: pointer to habanalabs device structure - * - * Install the relevant function pointers and call the early_init function, - * if such a function exists - */ -static int device_early_init(struct hl_device *hdev) -{ - int i, rc; - char workq_name[32]; - - switch (hdev->asic_type) { - case ASIC_GOYA: - goya_set_asic_funcs(hdev); - strlcpy(hdev->asic_name, "GOYA", sizeof(hdev->asic_name)); - break; - case ASIC_GAUDI: - gaudi_set_asic_funcs(hdev); - sprintf(hdev->asic_name, "GAUDI"); - break; - default: - dev_err(hdev->dev, "Unrecognized ASIC type %d\n", - hdev->asic_type); - return -EINVAL; - } - - rc = hdev->asic_funcs->early_init(hdev); - if (rc) - return rc; - - rc = hl_asid_init(hdev); - if (rc) - goto early_fini; - - if (hdev->asic_prop.completion_queues_count) { - hdev->cq_wq = kcalloc(hdev->asic_prop.completion_queues_count, - sizeof(*hdev->cq_wq), - GFP_ATOMIC); - if (!hdev->cq_wq) { - rc = -ENOMEM; - goto asid_fini; - } - } - - for (i = 0 ; i < hdev->asic_prop.completion_queues_count ; i++) { - snprintf(workq_name, 32, "hl-free-jobs-%u", i); - hdev->cq_wq[i] = create_singlethread_workqueue(workq_name); - if (hdev->cq_wq == NULL) { - dev_err(hdev->dev, "Failed to allocate CQ workqueue\n"); - rc = -ENOMEM; - goto free_cq_wq; - } - } - - hdev->eq_wq = alloc_workqueue("hl-events", WQ_UNBOUND, 0); - if (hdev->eq_wq == NULL) { - dev_err(hdev->dev, "Failed to allocate EQ workqueue\n"); - rc = -ENOMEM; - goto free_cq_wq; - } - - hdev->hl_chip_info = kzalloc(sizeof(struct hwmon_chip_info), - GFP_KERNEL); - if (!hdev->hl_chip_info) { - rc = -ENOMEM; - goto free_eq_wq; - } - - hdev->idle_busy_ts_arr = kmalloc_array(HL_IDLE_BUSY_TS_ARR_SIZE, - sizeof(struct hl_device_idle_busy_ts), - (GFP_KERNEL | __GFP_ZERO)); - if (!hdev->idle_busy_ts_arr) { - rc = -ENOMEM; - goto free_chip_info; - } - - hl_cb_mgr_init(&hdev->kernel_cb_mgr); - - mutex_init(&hdev->send_cpu_message_lock); - mutex_init(&hdev->debug_lock); - mutex_init(&hdev->mmu_cache_lock); - INIT_LIST_HEAD(&hdev->hw_queues_mirror_list); - spin_lock_init(&hdev->hw_queues_mirror_lock); - INIT_LIST_HEAD(&hdev->fpriv_list); - mutex_init(&hdev->fpriv_list_lock); - atomic_set(&hdev->in_reset, 0); - - return 0; - -free_chip_info: - kfree(hdev->hl_chip_info); -free_eq_wq: - destroy_workqueue(hdev->eq_wq); -free_cq_wq: - for (i = 0 ; i < hdev->asic_prop.completion_queues_count ; i++) - if (hdev->cq_wq[i]) - destroy_workqueue(hdev->cq_wq[i]); - kfree(hdev->cq_wq); -asid_fini: - hl_asid_fini(hdev); -early_fini: - if (hdev->asic_funcs->early_fini) - hdev->asic_funcs->early_fini(hdev); - - return rc; -} - -/* - * device_early_fini - finalize all that was done in device_early_init - * - * @hdev: pointer to habanalabs device structure - * - */ -static void device_early_fini(struct hl_device *hdev) -{ - int i; - - mutex_destroy(&hdev->mmu_cache_lock); - mutex_destroy(&hdev->debug_lock); - mutex_destroy(&hdev->send_cpu_message_lock); - - mutex_destroy(&hdev->fpriv_list_lock); - - hl_cb_mgr_fini(hdev, &hdev->kernel_cb_mgr); - - kfree(hdev->idle_busy_ts_arr); - kfree(hdev->hl_chip_info); - - destroy_workqueue(hdev->eq_wq); - - for (i = 0 ; i < hdev->asic_prop.completion_queues_count ; i++) - destroy_workqueue(hdev->cq_wq[i]); - kfree(hdev->cq_wq); - - hl_asid_fini(hdev); - - if (hdev->asic_funcs->early_fini) - hdev->asic_funcs->early_fini(hdev); -} - -static void set_freq_to_low_job(struct work_struct *work) -{ - struct hl_device *hdev = container_of(work, struct hl_device, - work_freq.work); - - mutex_lock(&hdev->fpriv_list_lock); - - if (!hdev->compute_ctx) - hl_device_set_frequency(hdev, PLL_LOW); - - mutex_unlock(&hdev->fpriv_list_lock); - - schedule_delayed_work(&hdev->work_freq, - usecs_to_jiffies(HL_PLL_LOW_JOB_FREQ_USEC)); -} - -static void hl_device_heartbeat(struct work_struct *work) -{ - struct hl_device *hdev = container_of(work, struct hl_device, - work_heartbeat.work); - - if (hl_device_disabled_or_in_reset(hdev)) - goto reschedule; - - if (!hdev->asic_funcs->send_heartbeat(hdev)) - goto reschedule; - - dev_err(hdev->dev, "Device heartbeat failed!\n"); - hl_device_reset(hdev, true, false); - - return; - -reschedule: - schedule_delayed_work(&hdev->work_heartbeat, - usecs_to_jiffies(HL_HEARTBEAT_PER_USEC)); -} - -/* - * device_late_init - do late stuff initialization for the habanalabs device - * - * @hdev: pointer to habanalabs device structure - * - * Do stuff that either needs the device H/W queues to be active or needs - * to happen after all the rest of the initialization is finished - */ -static int device_late_init(struct hl_device *hdev) -{ - int rc; - - if (hdev->asic_funcs->late_init) { - rc = hdev->asic_funcs->late_init(hdev); - if (rc) { - dev_err(hdev->dev, - "failed late initialization for the H/W\n"); - return rc; - } - } - - hdev->high_pll = hdev->asic_prop.high_pll; - - /* force setting to low frequency */ - hdev->curr_pll_profile = PLL_LOW; - - if (hdev->pm_mng_profile == PM_AUTO) - hdev->asic_funcs->set_pll_profile(hdev, PLL_LOW); - else - hdev->asic_funcs->set_pll_profile(hdev, PLL_LAST); - - INIT_DELAYED_WORK(&hdev->work_freq, set_freq_to_low_job); - schedule_delayed_work(&hdev->work_freq, - usecs_to_jiffies(HL_PLL_LOW_JOB_FREQ_USEC)); - - if (hdev->heartbeat) { - INIT_DELAYED_WORK(&hdev->work_heartbeat, hl_device_heartbeat); - schedule_delayed_work(&hdev->work_heartbeat, - usecs_to_jiffies(HL_HEARTBEAT_PER_USEC)); - } - - hdev->late_init_done = true; - - return 0; -} - -/* - * device_late_fini - finalize all that was done in device_late_init - * - * @hdev: pointer to habanalabs device structure - * - */ -static void device_late_fini(struct hl_device *hdev) -{ - if (!hdev->late_init_done) - return; - - cancel_delayed_work_sync(&hdev->work_freq); - if (hdev->heartbeat) - cancel_delayed_work_sync(&hdev->work_heartbeat); - - if (hdev->asic_funcs->late_fini) - hdev->asic_funcs->late_fini(hdev); - - hdev->late_init_done = false; -} - -uint32_t hl_device_utilization(struct hl_device *hdev, uint32_t period_ms) -{ - struct hl_device_idle_busy_ts *ts; - ktime_t zero_ktime, curr = ktime_get(); - u32 overlap_cnt = 0, last_index = hdev->idle_busy_ts_idx; - s64 period_us, last_start_us, last_end_us, last_busy_time_us, - total_busy_time_us = 0, total_busy_time_ms; - - zero_ktime = ktime_set(0, 0); - period_us = period_ms * USEC_PER_MSEC; - ts = &hdev->idle_busy_ts_arr[last_index]; - - /* check case that device is currently in idle */ - if (!ktime_compare(ts->busy_to_idle_ts, zero_ktime) && - !ktime_compare(ts->idle_to_busy_ts, zero_ktime)) { - - last_index--; - /* Handle case idle_busy_ts_idx was 0 */ - if (last_index > HL_IDLE_BUSY_TS_ARR_SIZE) - last_index = HL_IDLE_BUSY_TS_ARR_SIZE - 1; - - ts = &hdev->idle_busy_ts_arr[last_index]; - } - - while (overlap_cnt < HL_IDLE_BUSY_TS_ARR_SIZE) { - /* Check if we are in last sample case. i.e. if the sample - * begun before the sampling period. This could be a real - * sample or 0 so need to handle both cases - */ - last_start_us = ktime_to_us( - ktime_sub(curr, ts->idle_to_busy_ts)); - - if (last_start_us > period_us) { - - /* First check two cases: - * 1. If the device is currently busy - * 2. If the device was idle during the whole sampling - * period - */ - - if (!ktime_compare(ts->busy_to_idle_ts, zero_ktime)) { - /* Check if the device is currently busy */ - if (ktime_compare(ts->idle_to_busy_ts, - zero_ktime)) - return 100; - - /* We either didn't have any activity or we - * reached an entry which is 0. Either way, - * exit and return what was accumulated so far - */ - break; - } - - /* If sample has finished, check it is relevant */ - last_end_us = ktime_to_us( - ktime_sub(curr, ts->busy_to_idle_ts)); - - if (last_end_us > period_us) - break; - - /* It is relevant so add it but with adjustment */ - last_busy_time_us = ktime_to_us( - ktime_sub(ts->busy_to_idle_ts, - ts->idle_to_busy_ts)); - total_busy_time_us += last_busy_time_us - - (last_start_us - period_us); - break; - } - - /* Check if the sample is finished or still open */ - if (ktime_compare(ts->busy_to_idle_ts, zero_ktime)) - last_busy_time_us = ktime_to_us( - ktime_sub(ts->busy_to_idle_ts, - ts->idle_to_busy_ts)); - else - last_busy_time_us = ktime_to_us( - ktime_sub(curr, ts->idle_to_busy_ts)); - - total_busy_time_us += last_busy_time_us; - - last_index--; - /* Handle case idle_busy_ts_idx was 0 */ - if (last_index > HL_IDLE_BUSY_TS_ARR_SIZE) - last_index = HL_IDLE_BUSY_TS_ARR_SIZE - 1; - - ts = &hdev->idle_busy_ts_arr[last_index]; - - overlap_cnt++; - } - - total_busy_time_ms = DIV_ROUND_UP_ULL(total_busy_time_us, - USEC_PER_MSEC); - - return DIV_ROUND_UP_ULL(total_busy_time_ms * 100, period_ms); -} - -/* - * hl_device_set_frequency - set the frequency of the device - * - * @hdev: pointer to habanalabs device structure - * @freq: the new frequency value - * - * Change the frequency if needed. This function has no protection against - * concurrency, therefore it is assumed that the calling function has protected - * itself against the case of calling this function from multiple threads with - * different values - * - * Returns 0 if no change was done, otherwise returns 1 - */ -int hl_device_set_frequency(struct hl_device *hdev, enum hl_pll_frequency freq) -{ - if ((hdev->pm_mng_profile == PM_MANUAL) || - (hdev->curr_pll_profile == freq)) - return 0; - - dev_dbg(hdev->dev, "Changing device frequency to %s\n", - freq == PLL_HIGH ? "high" : "low"); - - hdev->asic_funcs->set_pll_profile(hdev, freq); - - hdev->curr_pll_profile = freq; - - return 1; -} - -int hl_device_set_debug_mode(struct hl_device *hdev, bool enable) -{ - int rc = 0; - - mutex_lock(&hdev->debug_lock); - - if (!enable) { - if (!hdev->in_debug) { - dev_err(hdev->dev, - "Failed to disable debug mode because device was not in debug mode\n"); - rc = -EFAULT; - goto out; - } - - if (!hdev->hard_reset_pending) - hdev->asic_funcs->halt_coresight(hdev); - - hdev->in_debug = 0; - - if (!hdev->hard_reset_pending) - hdev->asic_funcs->enable_clock_gating(hdev); - - goto out; - } - - if (hdev->in_debug) { - dev_err(hdev->dev, - "Failed to enable debug mode because device is already in debug mode\n"); - rc = -EFAULT; - goto out; - } - - hdev->asic_funcs->disable_clock_gating(hdev); - hdev->in_debug = 1; - -out: - mutex_unlock(&hdev->debug_lock); - - return rc; -} - -/* - * hl_device_suspend - initiate device suspend - * - * @hdev: pointer to habanalabs device structure - * - * Puts the hw in the suspend state (all asics). - * Returns 0 for success or an error on failure. - * Called at driver suspend. - */ -int hl_device_suspend(struct hl_device *hdev) -{ - int rc; - - pci_save_state(hdev->pdev); - - /* Block future CS/VM/JOB completion operations */ - rc = atomic_cmpxchg(&hdev->in_reset, 0, 1); - if (rc) { - dev_err(hdev->dev, "Can't suspend while in reset\n"); - return -EIO; - } - - /* This blocks all other stuff that is not blocked by in_reset */ - hdev->disabled = true; - - /* - * Flush anyone that is inside the critical section of enqueue - * jobs to the H/W - */ - hdev->asic_funcs->hw_queues_lock(hdev); - hdev->asic_funcs->hw_queues_unlock(hdev); - - /* Flush processes that are sending message to CPU */ - mutex_lock(&hdev->send_cpu_message_lock); - mutex_unlock(&hdev->send_cpu_message_lock); - - rc = hdev->asic_funcs->suspend(hdev); - if (rc) - dev_err(hdev->dev, - "Failed to disable PCI access of device CPU\n"); - - /* Shut down the device */ - pci_disable_device(hdev->pdev); - pci_set_power_state(hdev->pdev, PCI_D3hot); - - return 0; -} - -/* - * hl_device_resume - initiate device resume - * - * @hdev: pointer to habanalabs device structure - * - * Bring the hw back to operating state (all asics). - * Returns 0 for success or an error on failure. - * Called at driver resume. - */ -int hl_device_resume(struct hl_device *hdev) -{ - int rc; - - pci_set_power_state(hdev->pdev, PCI_D0); - pci_restore_state(hdev->pdev); - rc = pci_enable_device_mem(hdev->pdev); - if (rc) { - dev_err(hdev->dev, - "Failed to enable PCI device in resume\n"); - return rc; - } - - pci_set_master(hdev->pdev); - - rc = hdev->asic_funcs->resume(hdev); - if (rc) { - dev_err(hdev->dev, "Failed to resume device after suspend\n"); - goto disable_device; - } - - - hdev->disabled = false; - atomic_set(&hdev->in_reset, 0); - - rc = hl_device_reset(hdev, true, false); - if (rc) { - dev_err(hdev->dev, "Failed to reset device during resume\n"); - goto disable_device; - } - - return 0; - -disable_device: - pci_clear_master(hdev->pdev); - pci_disable_device(hdev->pdev); - - return rc; -} - -static int device_kill_open_processes(struct hl_device *hdev) -{ - u16 pending_total, pending_cnt; - struct hl_fpriv *hpriv; - struct task_struct *task = NULL; - - if (hdev->pldm) - pending_total = HL_PLDM_PENDING_RESET_PER_SEC; - else - pending_total = HL_PENDING_RESET_PER_SEC; - - /* Giving time for user to close FD, and for processes that are inside - * hl_device_open to finish - */ - if (!list_empty(&hdev->fpriv_list)) - ssleep(1); - - mutex_lock(&hdev->fpriv_list_lock); - - /* This section must be protected because we are dereferencing - * pointers that are freed if the process exits - */ - list_for_each_entry(hpriv, &hdev->fpriv_list, dev_node) { - task = get_pid_task(hpriv->taskpid, PIDTYPE_PID); - if (task) { - dev_info(hdev->dev, "Killing user process pid=%d\n", - task_pid_nr(task)); - send_sig(SIGKILL, task, 1); - usleep_range(1000, 10000); - - put_task_struct(task); - } - } - - mutex_unlock(&hdev->fpriv_list_lock); - - /* We killed the open users, but because the driver cleans up after the - * user contexts are closed (e.g. mmu mappings), we need to wait again - * to make sure the cleaning phase is finished before continuing with - * the reset - */ - - pending_cnt = pending_total; - - while ((!list_empty(&hdev->fpriv_list)) && (pending_cnt)) { - dev_info(hdev->dev, - "Waiting for all unmap operations to finish before hard reset\n"); - - pending_cnt--; - - ssleep(1); - } - - return list_empty(&hdev->fpriv_list) ? 0 : -EBUSY; -} - -static void device_hard_reset_pending(struct work_struct *work) -{ - struct hl_device_reset_work *device_reset_work = - container_of(work, struct hl_device_reset_work, reset_work); - struct hl_device *hdev = device_reset_work->hdev; - - hl_device_reset(hdev, true, true); - - kfree(device_reset_work); -} - -/* - * hl_device_reset - reset the device - * - * @hdev: pointer to habanalabs device structure - * @hard_reset: should we do hard reset to all engines or just reset the - * compute/dma engines - * @from_hard_reset_thread: is the caller the hard-reset thread - * - * Block future CS and wait for pending CS to be enqueued - * Call ASIC H/W fini - * Flush all completions - * Re-initialize all internal data structures - * Call ASIC H/W init, late_init - * Test queues - * Enable device - * - * Returns 0 for success or an error on failure. - */ -int hl_device_reset(struct hl_device *hdev, bool hard_reset, - bool from_hard_reset_thread) -{ - int i, rc; - - if (!hdev->init_done) { - dev_err(hdev->dev, - "Can't reset before initialization is done\n"); - return 0; - } - - if ((!hard_reset) && (!hdev->supports_soft_reset)) { - dev_dbg(hdev->dev, "Doing hard-reset instead of soft-reset\n"); - hard_reset = true; - } - - /* - * Prevent concurrency in this function - only one reset should be - * done at any given time. Only need to perform this if we didn't - * get from the dedicated hard reset thread - */ - if (!from_hard_reset_thread) { - /* Block future CS/VM/JOB completion operations */ - rc = atomic_cmpxchg(&hdev->in_reset, 0, 1); - if (rc) - return 0; - - if (hard_reset) { - /* Disable PCI access from device F/W so he won't send - * us additional interrupts. We disable MSI/MSI-X at - * the halt_engines function and we can't have the F/W - * sending us interrupts after that. We need to disable - * the access here because if the device is marked - * disable, the message won't be send. Also, in case - * of heartbeat, the device CPU is marked as disable - * so this message won't be sent - */ - if (hl_fw_send_pci_access_msg(hdev, - ARMCP_PACKET_DISABLE_PCI_ACCESS)) - dev_warn(hdev->dev, - "Failed to disable PCI access by F/W\n"); - } - - /* This also blocks future CS/VM/JOB completion operations */ - hdev->disabled = true; - - /* Flush anyone that is inside the critical section of enqueue - * jobs to the H/W - */ - hdev->asic_funcs->hw_queues_lock(hdev); - hdev->asic_funcs->hw_queues_unlock(hdev); - - /* Flush anyone that is inside device open */ - mutex_lock(&hdev->fpriv_list_lock); - mutex_unlock(&hdev->fpriv_list_lock); - - dev_err(hdev->dev, "Going to RESET device!\n"); - } - -again: - if ((hard_reset) && (!from_hard_reset_thread)) { - struct hl_device_reset_work *device_reset_work; - - hdev->hard_reset_pending = true; - - device_reset_work = kzalloc(sizeof(*device_reset_work), - GFP_ATOMIC); - if (!device_reset_work) { - rc = -ENOMEM; - goto out_err; - } - - /* - * Because the reset function can't run from interrupt or - * from heartbeat work, we need to call the reset function - * from a dedicated work - */ - INIT_WORK(&device_reset_work->reset_work, - device_hard_reset_pending); - device_reset_work->hdev = hdev; - schedule_work(&device_reset_work->reset_work); - - return 0; - } - - if (hard_reset) { - device_late_fini(hdev); - - /* - * Now that the heartbeat thread is closed, flush processes - * which are sending messages to CPU - */ - mutex_lock(&hdev->send_cpu_message_lock); - mutex_unlock(&hdev->send_cpu_message_lock); - } - - /* - * Halt the engines and disable interrupts so we won't get any more - * completions from H/W and we won't have any accesses from the - * H/W to the host machine - */ - hdev->asic_funcs->halt_engines(hdev, hard_reset); - - /* Go over all the queues, release all CS and their jobs */ - hl_cs_rollback_all(hdev); - - if (hard_reset) { - /* Kill processes here after CS rollback. This is because the - * process can't really exit until all its CSs are done, which - * is what we do in cs rollback - */ - rc = device_kill_open_processes(hdev); - if (rc) { - dev_crit(hdev->dev, - "Failed to kill all open processes, stopping hard reset\n"); - goto out_err; - } - - /* Flush the Event queue workers to make sure no other thread is - * reading or writing to registers during the reset - */ - flush_workqueue(hdev->eq_wq); - } - - /* Release kernel context */ - if ((hard_reset) && (hl_ctx_put(hdev->kernel_ctx) == 1)) - hdev->kernel_ctx = NULL; - - /* Reset the H/W. It will be in idle state after this returns */ - hdev->asic_funcs->hw_fini(hdev, hard_reset); - - if (hard_reset) { - hl_vm_fini(hdev); - hl_mmu_fini(hdev); - hl_eq_reset(hdev, &hdev->event_queue); - } - - /* Re-initialize PI,CI to 0 in all queues (hw queue, cq) */ - hl_hw_queue_reset(hdev, hard_reset); - for (i = 0 ; i < hdev->asic_prop.completion_queues_count ; i++) - hl_cq_reset(hdev, &hdev->completion_queue[i]); - - hdev->idle_busy_ts_idx = 0; - hdev->idle_busy_ts_arr[0].busy_to_idle_ts = ktime_set(0, 0); - hdev->idle_busy_ts_arr[0].idle_to_busy_ts = ktime_set(0, 0); - - if (hdev->cs_active_cnt) - dev_crit(hdev->dev, "CS active cnt %d is not 0 during reset\n", - hdev->cs_active_cnt); - - mutex_lock(&hdev->fpriv_list_lock); - - /* Make sure the context switch phase will run again */ - if (hdev->compute_ctx) { - atomic_set(&hdev->compute_ctx->thread_ctx_switch_token, 1); - hdev->compute_ctx->thread_ctx_switch_wait_token = 0; - } - - mutex_unlock(&hdev->fpriv_list_lock); - - /* Finished tear-down, starting to re-initialize */ - - if (hard_reset) { - hdev->device_cpu_disabled = false; - hdev->hard_reset_pending = false; - - if (hdev->kernel_ctx) { - dev_crit(hdev->dev, - "kernel ctx was alive during hard reset, something is terribly wrong\n"); - rc = -EBUSY; - goto out_err; - } - - rc = hl_mmu_init(hdev); - if (rc) { - dev_err(hdev->dev, - "Failed to initialize MMU S/W after hard reset\n"); - goto out_err; - } - - /* Allocate the kernel context */ - hdev->kernel_ctx = kzalloc(sizeof(*hdev->kernel_ctx), - GFP_KERNEL); - if (!hdev->kernel_ctx) { - rc = -ENOMEM; - goto out_err; - } - - hdev->compute_ctx = NULL; - - rc = hl_ctx_init(hdev, hdev->kernel_ctx, true); - if (rc) { - dev_err(hdev->dev, - "failed to init kernel ctx in hard reset\n"); - kfree(hdev->kernel_ctx); - hdev->kernel_ctx = NULL; - goto out_err; - } - } - - rc = hdev->asic_funcs->hw_init(hdev); - if (rc) { - dev_err(hdev->dev, - "failed to initialize the H/W after reset\n"); - goto out_err; - } - - hdev->disabled = false; - - /* Check that the communication with the device is working */ - rc = hdev->asic_funcs->test_queues(hdev); - if (rc) { - dev_err(hdev->dev, - "Failed to detect if device is alive after reset\n"); - goto out_err; - } - - if (hard_reset) { - rc = device_late_init(hdev); - if (rc) { - dev_err(hdev->dev, - "Failed late init after hard reset\n"); - goto out_err; - } - - rc = hl_vm_init(hdev); - if (rc) { - dev_err(hdev->dev, - "Failed to init memory module after hard reset\n"); - goto out_err; - } - - hl_set_max_power(hdev, hdev->max_power); - } else { - rc = hdev->asic_funcs->soft_reset_late_init(hdev); - if (rc) { - dev_err(hdev->dev, - "Failed late init after soft reset\n"); - goto out_err; - } - } - - atomic_set(&hdev->in_reset, 0); - - if (hard_reset) - hdev->hard_reset_cnt++; - else - hdev->soft_reset_cnt++; - - dev_warn(hdev->dev, "Successfully finished resetting the device\n"); - - return 0; - -out_err: - hdev->disabled = true; - - if (hard_reset) { - dev_err(hdev->dev, - "Failed to reset! Device is NOT usable\n"); - hdev->hard_reset_cnt++; - } else { - dev_err(hdev->dev, - "Failed to do soft-reset, trying hard reset\n"); - hdev->soft_reset_cnt++; - hard_reset = true; - goto again; - } - - atomic_set(&hdev->in_reset, 0); - - return rc; -} - -/* - * hl_device_init - main initialization function for habanalabs device - * - * @hdev: pointer to habanalabs device structure - * - * Allocate an id for the device, do early initialization and then call the - * ASIC specific initialization functions. Finally, create the cdev and the - * Linux device to expose it to the user - */ -int hl_device_init(struct hl_device *hdev, struct class *hclass) -{ - int i, rc, cq_cnt, cq_ready_cnt; - char *name; - bool add_cdev_sysfs_on_err = false; - - name = kasprintf(GFP_KERNEL, "hl%d", hdev->id / 2); - if (!name) { - rc = -ENOMEM; - goto out_disabled; - } - - /* Initialize cdev and device structures */ - rc = device_init_cdev(hdev, hclass, hdev->id, &hl_ops, name, - &hdev->cdev, &hdev->dev); - - kfree(name); - - if (rc) - goto out_disabled; - - name = kasprintf(GFP_KERNEL, "hl_controlD%d", hdev->id / 2); - if (!name) { - rc = -ENOMEM; - goto free_dev; - } - - /* Initialize cdev and device structures for control device */ - rc = device_init_cdev(hdev, hclass, hdev->id_control, &hl_ctrl_ops, - name, &hdev->cdev_ctrl, &hdev->dev_ctrl); - - kfree(name); - - if (rc) - goto free_dev; - - /* Initialize ASIC function pointers and perform early init */ - rc = device_early_init(hdev); - if (rc) - goto free_dev_ctrl; - - /* - * Start calling ASIC initialization. First S/W then H/W and finally - * late init - */ - rc = hdev->asic_funcs->sw_init(hdev); - if (rc) - goto early_fini; - - /* - * Initialize the H/W queues. Must be done before hw_init, because - * there the addresses of the kernel queue are being written to the - * registers of the device - */ - rc = hl_hw_queues_create(hdev); - if (rc) { - dev_err(hdev->dev, "failed to initialize kernel queues\n"); - goto sw_fini; - } - - cq_cnt = hdev->asic_prop.completion_queues_count; - - /* - * Initialize the completion queues. Must be done before hw_init, - * because there the addresses of the completion queues are being - * passed as arguments to request_irq - */ - if (cq_cnt) { - hdev->completion_queue = kcalloc(cq_cnt, - sizeof(*hdev->completion_queue), - GFP_KERNEL); - - if (!hdev->completion_queue) { - dev_err(hdev->dev, - "failed to allocate completion queues\n"); - rc = -ENOMEM; - goto hw_queues_destroy; - } - } - - for (i = 0, cq_ready_cnt = 0 ; i < cq_cnt ; i++, cq_ready_cnt++) { - rc = hl_cq_init(hdev, &hdev->completion_queue[i], - hdev->asic_funcs->get_queue_id_for_cq(hdev, i)); - if (rc) { - dev_err(hdev->dev, - "failed to initialize completion queue\n"); - goto cq_fini; - } - hdev->completion_queue[i].cq_idx = i; - } - - /* - * Initialize the event queue. Must be done before hw_init, - * because there the address of the event queue is being - * passed as argument to request_irq - */ - rc = hl_eq_init(hdev, &hdev->event_queue); - if (rc) { - dev_err(hdev->dev, "failed to initialize event queue\n"); - goto cq_fini; - } - - /* MMU S/W must be initialized before kernel context is created */ - rc = hl_mmu_init(hdev); - if (rc) { - dev_err(hdev->dev, "Failed to initialize MMU S/W structures\n"); - goto eq_fini; - } - - /* Allocate the kernel context */ - hdev->kernel_ctx = kzalloc(sizeof(*hdev->kernel_ctx), GFP_KERNEL); - if (!hdev->kernel_ctx) { - rc = -ENOMEM; - goto mmu_fini; - } - - hdev->compute_ctx = NULL; - - rc = hl_ctx_init(hdev, hdev->kernel_ctx, true); - if (rc) { - dev_err(hdev->dev, "failed to initialize kernel context\n"); - kfree(hdev->kernel_ctx); - goto mmu_fini; - } - - rc = hl_cb_pool_init(hdev); - if (rc) { - dev_err(hdev->dev, "failed to initialize CB pool\n"); - goto release_ctx; - } - - hl_debugfs_add_device(hdev); - - if (hdev->asic_funcs->get_hw_state(hdev) == HL_DEVICE_HW_STATE_DIRTY) { - dev_info(hdev->dev, - "H/W state is dirty, must reset before initializing\n"); - hdev->asic_funcs->halt_engines(hdev, true); - hdev->asic_funcs->hw_fini(hdev, true); - } - - /* - * From this point, in case of an error, add char devices and create - * sysfs nodes as part of the error flow, to allow debugging. - */ - add_cdev_sysfs_on_err = true; - - rc = hdev->asic_funcs->hw_init(hdev); - if (rc) { - dev_err(hdev->dev, "failed to initialize the H/W\n"); - rc = 0; - goto out_disabled; - } - - hdev->disabled = false; - - /* Check that the communication with the device is working */ - rc = hdev->asic_funcs->test_queues(hdev); - if (rc) { - dev_err(hdev->dev, "Failed to detect if device is alive\n"); - rc = 0; - goto out_disabled; - } - - rc = device_late_init(hdev); - if (rc) { - dev_err(hdev->dev, "Failed late initialization\n"); - rc = 0; - goto out_disabled; - } - - dev_info(hdev->dev, "Found %s device with %lluGB DRAM\n", - hdev->asic_name, - hdev->asic_prop.dram_size / 1024 / 1024 / 1024); - - rc = hl_vm_init(hdev); - if (rc) { - dev_err(hdev->dev, "Failed to initialize memory module\n"); - rc = 0; - goto out_disabled; - } - - /* - * Expose devices and sysfs nodes to user. - * From here there is no need to add char devices and create sysfs nodes - * in case of an error. - */ - add_cdev_sysfs_on_err = false; - rc = device_cdev_sysfs_add(hdev); - if (rc) { - dev_err(hdev->dev, - "Failed to add char devices and sysfs nodes\n"); - rc = 0; - goto out_disabled; - } - - /* - * hl_hwmon_init() must be called after device_late_init(), because only - * there we get the information from the device about which - * hwmon-related sensors the device supports. - * Furthermore, it must be done after adding the device to the system. - */ - rc = hl_hwmon_init(hdev); - if (rc) { - dev_err(hdev->dev, "Failed to initialize hwmon\n"); - rc = 0; - goto out_disabled; - } - - dev_notice(hdev->dev, - "Successfully added device to habanalabs driver\n"); - - hdev->init_done = true; - - return 0; - -release_ctx: - if (hl_ctx_put(hdev->kernel_ctx) != 1) - dev_err(hdev->dev, - "kernel ctx is still alive on initialization failure\n"); -mmu_fini: - hl_mmu_fini(hdev); -eq_fini: - hl_eq_fini(hdev, &hdev->event_queue); -cq_fini: - for (i = 0 ; i < cq_ready_cnt ; i++) - hl_cq_fini(hdev, &hdev->completion_queue[i]); - kfree(hdev->completion_queue); -hw_queues_destroy: - hl_hw_queues_destroy(hdev); -sw_fini: - hdev->asic_funcs->sw_fini(hdev); -early_fini: - device_early_fini(hdev); -free_dev_ctrl: - kfree(hdev->dev_ctrl); -free_dev: - kfree(hdev->dev); -out_disabled: - hdev->disabled = true; - if (add_cdev_sysfs_on_err) - device_cdev_sysfs_add(hdev); - if (hdev->pdev) - dev_err(&hdev->pdev->dev, - "Failed to initialize hl%d. Device is NOT usable !\n", - hdev->id / 2); - else - pr_err("Failed to initialize hl%d. Device is NOT usable !\n", - hdev->id / 2); - - return rc; -} - -/* - * hl_device_fini - main tear-down function for habanalabs device - * - * @hdev: pointer to habanalabs device structure - * - * Destroy the device, call ASIC fini functions and release the id - */ -void hl_device_fini(struct hl_device *hdev) -{ - int i, rc; - ktime_t timeout; - - dev_info(hdev->dev, "Removing device\n"); - - /* - * This function is competing with the reset function, so try to - * take the reset atomic and if we are already in middle of reset, - * wait until reset function is finished. Reset function is designed - * to always finish. However, in Gaudi, because of all the network - * ports, the hard reset could take between 10-30 seconds - */ - - timeout = ktime_add_us(ktime_get(), - HL_HARD_RESET_MAX_TIMEOUT * 1000 * 1000); - rc = atomic_cmpxchg(&hdev->in_reset, 0, 1); - while (rc) { - usleep_range(50, 200); - rc = atomic_cmpxchg(&hdev->in_reset, 0, 1); - if (ktime_compare(ktime_get(), timeout) > 0) { - WARN(1, "Failed to remove device because reset function did not finish\n"); - return; - } - } - - /* Mark device as disabled */ - hdev->disabled = true; - - /* Flush anyone that is inside the critical section of enqueue - * jobs to the H/W - */ - hdev->asic_funcs->hw_queues_lock(hdev); - hdev->asic_funcs->hw_queues_unlock(hdev); - - /* Flush anyone that is inside device open */ - mutex_lock(&hdev->fpriv_list_lock); - mutex_unlock(&hdev->fpriv_list_lock); - - hdev->hard_reset_pending = true; - - hl_hwmon_fini(hdev); - - device_late_fini(hdev); - - hl_debugfs_remove_device(hdev); - - /* - * Halt the engines and disable interrupts so we won't get any more - * completions from H/W and we won't have any accesses from the - * H/W to the host machine - */ - hdev->asic_funcs->halt_engines(hdev, true); - - /* Go over all the queues, release all CS and their jobs */ - hl_cs_rollback_all(hdev); - - /* Kill processes here after CS rollback. This is because the process - * can't really exit until all its CSs are done, which is what we - * do in cs rollback - */ - rc = device_kill_open_processes(hdev); - if (rc) - dev_crit(hdev->dev, "Failed to kill all open processes\n"); - - hl_cb_pool_fini(hdev); - - /* Release kernel context */ - if ((hdev->kernel_ctx) && (hl_ctx_put(hdev->kernel_ctx) != 1)) - dev_err(hdev->dev, "kernel ctx is still alive\n"); - - /* Reset the H/W. It will be in idle state after this returns */ - hdev->asic_funcs->hw_fini(hdev, true); - - hl_vm_fini(hdev); - - hl_mmu_fini(hdev); - - hl_eq_fini(hdev, &hdev->event_queue); - - for (i = 0 ; i < hdev->asic_prop.completion_queues_count ; i++) - hl_cq_fini(hdev, &hdev->completion_queue[i]); - kfree(hdev->completion_queue); - - hl_hw_queues_destroy(hdev); - - /* Call ASIC S/W finalize function */ - hdev->asic_funcs->sw_fini(hdev); - - device_early_fini(hdev); - - /* Hide devices and sysfs nodes from user */ - device_cdev_sysfs_del(hdev); - - pr_info("removed device successfully\n"); -} - -/* - * MMIO register access helper functions. - */ - -/* - * hl_rreg - Read an MMIO register - * - * @hdev: pointer to habanalabs device structure - * @reg: MMIO register offset (in bytes) - * - * Returns the value of the MMIO register we are asked to read - * - */ -inline u32 hl_rreg(struct hl_device *hdev, u32 reg) -{ - return readl(hdev->rmmio + reg); -} - -/* - * hl_wreg - Write to an MMIO register - * - * @hdev: pointer to habanalabs device structure - * @reg: MMIO register offset (in bytes) - * @val: 32-bit value - * - * Writes the 32-bit value into the MMIO register - * - */ -inline void hl_wreg(struct hl_device *hdev, u32 reg, u32 val) -{ - writel(val, hdev->rmmio + reg); -} diff --git a/drivers/misc/habanalabs/firmware_if.c b/drivers/misc/habanalabs/firmware_if.c deleted file mode 100644 index 3be1549cd137..000000000000 --- a/drivers/misc/habanalabs/firmware_if.c +++ /dev/null @@ -1,589 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -/* - * Copyright 2016-2019 HabanaLabs, Ltd. - * All Rights Reserved. - */ - -#include "habanalabs.h" -#include "include/hl_boot_if.h" - -#include -#include -#include -#include - -/** - * hl_fw_load_fw_to_device() - Load F/W code to device's memory. - * - * @hdev: pointer to hl_device structure. - * @fw_name: the firmware image name - * @dst: IO memory mapped address space to copy firmware to - * - * Copy fw code from firmware file to device memory. - * - * Return: 0 on success, non-zero for failure. - */ -int hl_fw_load_fw_to_device(struct hl_device *hdev, const char *fw_name, - void __iomem *dst) -{ - const struct firmware *fw; - const u64 *fw_data; - size_t fw_size; - int rc; - - rc = request_firmware(&fw, fw_name, hdev->dev); - if (rc) { - dev_err(hdev->dev, "Firmware file %s is not found!\n", fw_name); - goto out; - } - - fw_size = fw->size; - if ((fw_size % 4) != 0) { - dev_err(hdev->dev, "Illegal %s firmware size %zu\n", - fw_name, fw_size); - rc = -EINVAL; - goto out; - } - - dev_dbg(hdev->dev, "%s firmware size == %zu\n", fw_name, fw_size); - - fw_data = (const u64 *) fw->data; - - memcpy_toio(dst, fw_data, fw_size); - -out: - release_firmware(fw); - return rc; -} - -int hl_fw_send_pci_access_msg(struct hl_device *hdev, u32 opcode) -{ - struct armcp_packet pkt = {}; - - pkt.ctl = cpu_to_le32(opcode << ARMCP_PKT_CTL_OPCODE_SHIFT); - - return hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, - sizeof(pkt), HL_DEVICE_TIMEOUT_USEC, NULL); -} - -int hl_fw_send_cpu_message(struct hl_device *hdev, u32 hw_queue_id, u32 *msg, - u16 len, u32 timeout, long *result) -{ - struct armcp_packet *pkt; - dma_addr_t pkt_dma_addr; - u32 tmp; - int rc = 0; - - pkt = hdev->asic_funcs->cpu_accessible_dma_pool_alloc(hdev, len, - &pkt_dma_addr); - if (!pkt) { - dev_err(hdev->dev, - "Failed to allocate DMA memory for packet to CPU\n"); - return -ENOMEM; - } - - memcpy(pkt, msg, len); - - mutex_lock(&hdev->send_cpu_message_lock); - - if (hdev->disabled) - goto out; - - if (hdev->device_cpu_disabled) { - rc = -EIO; - goto out; - } - - rc = hl_hw_queue_send_cb_no_cmpl(hdev, hw_queue_id, len, pkt_dma_addr); - if (rc) { - dev_err(hdev->dev, "Failed to send CB on CPU PQ (%d)\n", rc); - goto out; - } - - rc = hl_poll_timeout_memory(hdev, &pkt->fence, tmp, - (tmp == ARMCP_PACKET_FENCE_VAL), 1000, - timeout, true); - - hl_hw_queue_inc_ci_kernel(hdev, hw_queue_id); - - if (rc == -ETIMEDOUT) { - dev_err(hdev->dev, "Device CPU packet timeout (0x%x)\n", tmp); - hdev->device_cpu_disabled = true; - goto out; - } - - tmp = le32_to_cpu(pkt->ctl); - - rc = (tmp & ARMCP_PKT_CTL_RC_MASK) >> ARMCP_PKT_CTL_RC_SHIFT; - if (rc) { - dev_err(hdev->dev, "F/W ERROR %d for CPU packet %d\n", - rc, - (tmp & ARMCP_PKT_CTL_OPCODE_MASK) - >> ARMCP_PKT_CTL_OPCODE_SHIFT); - rc = -EIO; - } else if (result) { - *result = (long) le64_to_cpu(pkt->result); - } - -out: - mutex_unlock(&hdev->send_cpu_message_lock); - - hdev->asic_funcs->cpu_accessible_dma_pool_free(hdev, len, pkt); - - return rc; -} - -int hl_fw_unmask_irq(struct hl_device *hdev, u16 event_type) -{ - struct armcp_packet pkt; - long result; - int rc; - - memset(&pkt, 0, sizeof(pkt)); - - pkt.ctl = cpu_to_le32(ARMCP_PACKET_UNMASK_RAZWI_IRQ << - ARMCP_PKT_CTL_OPCODE_SHIFT); - pkt.value = cpu_to_le64(event_type); - - rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), - HL_DEVICE_TIMEOUT_USEC, &result); - - if (rc) - dev_err(hdev->dev, "failed to unmask RAZWI IRQ %d", event_type); - - return rc; -} - -int hl_fw_unmask_irq_arr(struct hl_device *hdev, const u32 *irq_arr, - size_t irq_arr_size) -{ - struct armcp_unmask_irq_arr_packet *pkt; - size_t total_pkt_size; - long result; - int rc; - - total_pkt_size = sizeof(struct armcp_unmask_irq_arr_packet) + - irq_arr_size; - - /* data should be aligned to 8 bytes in order to ArmCP to copy it */ - total_pkt_size = (total_pkt_size + 0x7) & ~0x7; - - /* total_pkt_size is casted to u16 later on */ - if (total_pkt_size > USHRT_MAX) { - dev_err(hdev->dev, "too many elements in IRQ array\n"); - return -EINVAL; - } - - pkt = kzalloc(total_pkt_size, GFP_KERNEL); - if (!pkt) - return -ENOMEM; - - pkt->length = cpu_to_le32(irq_arr_size / sizeof(irq_arr[0])); - memcpy(&pkt->irqs, irq_arr, irq_arr_size); - - pkt->armcp_pkt.ctl = cpu_to_le32(ARMCP_PACKET_UNMASK_RAZWI_IRQ_ARRAY << - ARMCP_PKT_CTL_OPCODE_SHIFT); - - rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) pkt, - total_pkt_size, HL_DEVICE_TIMEOUT_USEC, &result); - - if (rc) - dev_err(hdev->dev, "failed to unmask IRQ array\n"); - - kfree(pkt); - - return rc; -} - -int hl_fw_test_cpu_queue(struct hl_device *hdev) -{ - struct armcp_packet test_pkt = {}; - long result; - int rc; - - test_pkt.ctl = cpu_to_le32(ARMCP_PACKET_TEST << - ARMCP_PKT_CTL_OPCODE_SHIFT); - test_pkt.value = cpu_to_le64(ARMCP_PACKET_FENCE_VAL); - - rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &test_pkt, - sizeof(test_pkt), HL_DEVICE_TIMEOUT_USEC, &result); - - if (!rc) { - if (result != ARMCP_PACKET_FENCE_VAL) - dev_err(hdev->dev, - "CPU queue test failed (0x%08lX)\n", result); - } else { - dev_err(hdev->dev, "CPU queue test failed, error %d\n", rc); - } - - return rc; -} - -void *hl_fw_cpu_accessible_dma_pool_alloc(struct hl_device *hdev, size_t size, - dma_addr_t *dma_handle) -{ - u64 kernel_addr; - - kernel_addr = gen_pool_alloc(hdev->cpu_accessible_dma_pool, size); - - *dma_handle = hdev->cpu_accessible_dma_address + - (kernel_addr - (u64) (uintptr_t) hdev->cpu_accessible_dma_mem); - - return (void *) (uintptr_t) kernel_addr; -} - -void hl_fw_cpu_accessible_dma_pool_free(struct hl_device *hdev, size_t size, - void *vaddr) -{ - gen_pool_free(hdev->cpu_accessible_dma_pool, (u64) (uintptr_t) vaddr, - size); -} - -int hl_fw_send_heartbeat(struct hl_device *hdev) -{ - struct armcp_packet hb_pkt = {}; - long result; - int rc; - - hb_pkt.ctl = cpu_to_le32(ARMCP_PACKET_TEST << - ARMCP_PKT_CTL_OPCODE_SHIFT); - hb_pkt.value = cpu_to_le64(ARMCP_PACKET_FENCE_VAL); - - rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &hb_pkt, - sizeof(hb_pkt), HL_DEVICE_TIMEOUT_USEC, &result); - - if ((rc) || (result != ARMCP_PACKET_FENCE_VAL)) - rc = -EIO; - - return rc; -} - -int hl_fw_armcp_info_get(struct hl_device *hdev) -{ - struct asic_fixed_properties *prop = &hdev->asic_prop; - struct armcp_packet pkt = {}; - void *armcp_info_cpu_addr; - dma_addr_t armcp_info_dma_addr; - long result; - int rc; - - armcp_info_cpu_addr = - hdev->asic_funcs->cpu_accessible_dma_pool_alloc(hdev, - sizeof(struct armcp_info), - &armcp_info_dma_addr); - if (!armcp_info_cpu_addr) { - dev_err(hdev->dev, - "Failed to allocate DMA memory for ArmCP info packet\n"); - return -ENOMEM; - } - - memset(armcp_info_cpu_addr, 0, sizeof(struct armcp_info)); - - pkt.ctl = cpu_to_le32(ARMCP_PACKET_INFO_GET << - ARMCP_PKT_CTL_OPCODE_SHIFT); - pkt.addr = cpu_to_le64(armcp_info_dma_addr); - pkt.data_max_size = cpu_to_le32(sizeof(struct armcp_info)); - - rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), - HL_ARMCP_INFO_TIMEOUT_USEC, &result); - if (rc) { - dev_err(hdev->dev, - "Failed to handle ArmCP info pkt, error %d\n", rc); - goto out; - } - - memcpy(&prop->armcp_info, armcp_info_cpu_addr, - sizeof(prop->armcp_info)); - - rc = hl_build_hwmon_channel_info(hdev, prop->armcp_info.sensors); - if (rc) { - dev_err(hdev->dev, - "Failed to build hwmon channel info, error %d\n", rc); - rc = -EFAULT; - goto out; - } - -out: - hdev->asic_funcs->cpu_accessible_dma_pool_free(hdev, - sizeof(struct armcp_info), armcp_info_cpu_addr); - - return rc; -} - -int hl_fw_get_eeprom_data(struct hl_device *hdev, void *data, size_t max_size) -{ - struct armcp_packet pkt = {}; - void *eeprom_info_cpu_addr; - dma_addr_t eeprom_info_dma_addr; - long result; - int rc; - - eeprom_info_cpu_addr = - hdev->asic_funcs->cpu_accessible_dma_pool_alloc(hdev, - max_size, &eeprom_info_dma_addr); - if (!eeprom_info_cpu_addr) { - dev_err(hdev->dev, - "Failed to allocate DMA memory for ArmCP EEPROM packet\n"); - return -ENOMEM; - } - - memset(eeprom_info_cpu_addr, 0, max_size); - - pkt.ctl = cpu_to_le32(ARMCP_PACKET_EEPROM_DATA_GET << - ARMCP_PKT_CTL_OPCODE_SHIFT); - pkt.addr = cpu_to_le64(eeprom_info_dma_addr); - pkt.data_max_size = cpu_to_le32(max_size); - - rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), - HL_ARMCP_EEPROM_TIMEOUT_USEC, &result); - - if (rc) { - dev_err(hdev->dev, - "Failed to handle ArmCP EEPROM packet, error %d\n", rc); - goto out; - } - - /* result contains the actual size */ - memcpy(data, eeprom_info_cpu_addr, min((size_t)result, max_size)); - -out: - hdev->asic_funcs->cpu_accessible_dma_pool_free(hdev, max_size, - eeprom_info_cpu_addr); - - return rc; -} - -static void fw_read_errors(struct hl_device *hdev, u32 boot_err0_reg) -{ - u32 err_val; - - /* Some of the firmware status codes are deprecated in newer f/w - * versions. In those versions, the errors are reported - * in different registers. Therefore, we need to check those - * registers and print the exact errors. Moreover, there - * may be multiple errors, so we need to report on each error - * separately. Some of the error codes might indicate a state - * that is not an error per-se, but it is an error in production - * environment - */ - err_val = RREG32(boot_err0_reg); - if (!(err_val & CPU_BOOT_ERR0_ENABLED)) - return; - - if (err_val & CPU_BOOT_ERR0_DRAM_INIT_FAIL) - dev_err(hdev->dev, - "Device boot error - DRAM initialization failed\n"); - if (err_val & CPU_BOOT_ERR0_FIT_CORRUPTED) - dev_err(hdev->dev, "Device boot error - FIT image corrupted\n"); - if (err_val & CPU_BOOT_ERR0_TS_INIT_FAIL) - dev_err(hdev->dev, - "Device boot error - Thermal Sensor initialization failed\n"); - if (err_val & CPU_BOOT_ERR0_DRAM_SKIPPED) - dev_warn(hdev->dev, - "Device boot warning - Skipped DRAM initialization\n"); - if (err_val & CPU_BOOT_ERR0_BMC_WAIT_SKIPPED) - dev_warn(hdev->dev, - "Device boot error - Skipped waiting for BMC\n"); - if (err_val & CPU_BOOT_ERR0_NIC_DATA_NOT_RDY) - dev_err(hdev->dev, - "Device boot error - Serdes data from BMC not available\n"); - if (err_val & CPU_BOOT_ERR0_NIC_FW_FAIL) - dev_err(hdev->dev, - "Device boot error - NIC F/W initialization failed\n"); -} - -static void hl_detect_cpu_boot_status(struct hl_device *hdev, u32 status) -{ - switch (status) { - case CPU_BOOT_STATUS_NA: - dev_err(hdev->dev, - "Device boot error - BTL did NOT run\n"); - break; - case CPU_BOOT_STATUS_IN_WFE: - dev_err(hdev->dev, - "Device boot error - Stuck inside WFE loop\n"); - break; - case CPU_BOOT_STATUS_IN_BTL: - dev_err(hdev->dev, - "Device boot error - Stuck in BTL\n"); - break; - case CPU_BOOT_STATUS_IN_PREBOOT: - dev_err(hdev->dev, - "Device boot error - Stuck in Preboot\n"); - break; - case CPU_BOOT_STATUS_IN_SPL: - dev_err(hdev->dev, - "Device boot error - Stuck in SPL\n"); - break; - case CPU_BOOT_STATUS_IN_UBOOT: - dev_err(hdev->dev, - "Device boot error - Stuck in u-boot\n"); - break; - case CPU_BOOT_STATUS_DRAM_INIT_FAIL: - dev_err(hdev->dev, - "Device boot error - DRAM initialization failed\n"); - break; - case CPU_BOOT_STATUS_UBOOT_NOT_READY: - dev_err(hdev->dev, - "Device boot error - u-boot stopped by user\n"); - break; - case CPU_BOOT_STATUS_TS_INIT_FAIL: - dev_err(hdev->dev, - "Device boot error - Thermal Sensor initialization failed\n"); - break; - default: - dev_err(hdev->dev, - "Device boot error - Invalid status code %d\n", - status); - break; - } -} - -int hl_fw_init_cpu(struct hl_device *hdev, u32 cpu_boot_status_reg, - u32 msg_to_cpu_reg, u32 cpu_msg_status_reg, - u32 boot_err0_reg, bool skip_bmc, - u32 cpu_timeout, u32 boot_fit_timeout) -{ - u32 status; - int rc; - - dev_info(hdev->dev, "Going to wait for device boot (up to %lds)\n", - cpu_timeout / USEC_PER_SEC); - - /* Wait for boot FIT request */ - rc = hl_poll_timeout( - hdev, - cpu_boot_status_reg, - status, - status == CPU_BOOT_STATUS_WAITING_FOR_BOOT_FIT, - 10000, - boot_fit_timeout); - - if (rc) { - dev_dbg(hdev->dev, - "No boot fit request received, resuming boot\n"); - } else { - rc = hdev->asic_funcs->load_boot_fit_to_device(hdev); - if (rc) - goto out; - - /* Clear device CPU message status */ - WREG32(cpu_msg_status_reg, CPU_MSG_CLR); - - /* Signal device CPU that boot loader is ready */ - WREG32(msg_to_cpu_reg, KMD_MSG_FIT_RDY); - - /* Poll for CPU device ack */ - rc = hl_poll_timeout( - hdev, - cpu_msg_status_reg, - status, - status == CPU_MSG_OK, - 10000, - boot_fit_timeout); - - if (rc) { - dev_err(hdev->dev, - "Timeout waiting for boot fit load ack\n"); - goto out; - } - - /* Clear message */ - WREG32(msg_to_cpu_reg, KMD_MSG_NA); - } - - /* Make sure CPU boot-loader is running */ - rc = hl_poll_timeout( - hdev, - cpu_boot_status_reg, - status, - (status == CPU_BOOT_STATUS_DRAM_RDY) || - (status == CPU_BOOT_STATUS_NIC_FW_RDY) || - (status == CPU_BOOT_STATUS_READY_TO_BOOT) || - (status == CPU_BOOT_STATUS_SRAM_AVAIL), - 10000, - cpu_timeout); - - /* Read U-Boot, preboot versions now in case we will later fail */ - hdev->asic_funcs->read_device_fw_version(hdev, FW_COMP_UBOOT); - hdev->asic_funcs->read_device_fw_version(hdev, FW_COMP_PREBOOT); - - /* Some of the status codes below are deprecated in newer f/w - * versions but we keep them here for backward compatibility - */ - if (rc) { - hl_detect_cpu_boot_status(hdev, status); - rc = -EIO; - goto out; - } - - if (!hdev->fw_loading) { - dev_info(hdev->dev, "Skip loading FW\n"); - goto out; - } - - if (status == CPU_BOOT_STATUS_SRAM_AVAIL) - goto out; - - dev_info(hdev->dev, - "Loading firmware to device, may take some time...\n"); - - rc = hdev->asic_funcs->load_firmware_to_device(hdev); - if (rc) - goto out; - - if (skip_bmc) { - WREG32(msg_to_cpu_reg, KMD_MSG_SKIP_BMC); - - rc = hl_poll_timeout( - hdev, - cpu_boot_status_reg, - status, - (status == CPU_BOOT_STATUS_BMC_WAITING_SKIPPED), - 10000, - cpu_timeout); - - if (rc) { - dev_err(hdev->dev, - "Failed to get ACK on skipping BMC, %d\n", - status); - WREG32(msg_to_cpu_reg, KMD_MSG_NA); - rc = -EIO; - goto out; - } - } - - WREG32(msg_to_cpu_reg, KMD_MSG_FIT_RDY); - - rc = hl_poll_timeout( - hdev, - cpu_boot_status_reg, - status, - (status == CPU_BOOT_STATUS_SRAM_AVAIL), - 10000, - cpu_timeout); - - /* Clear message */ - WREG32(msg_to_cpu_reg, KMD_MSG_NA); - - if (rc) { - if (status == CPU_BOOT_STATUS_FIT_CORRUPTED) - dev_err(hdev->dev, - "Device reports FIT image is corrupted\n"); - else - dev_err(hdev->dev, - "Failed to load firmware to device, %d\n", - status); - - rc = -EIO; - goto out; - } - - dev_info(hdev->dev, "Successfully loaded firmware to device\n"); - -out: - fw_read_errors(hdev, boot_err0_reg); - - return rc; -} diff --git a/drivers/misc/habanalabs/gaudi/Makefile b/drivers/misc/habanalabs/gaudi/Makefile index f802cdc980ca..75104ae74e2b 100644 --- a/drivers/misc/habanalabs/gaudi/Makefile +++ b/drivers/misc/habanalabs/gaudi/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only -subdir-ccflags-y += -I$(src) +subdir-ccflags-y += -I$(src)/common HL_GAUDI_FILES := gaudi/gaudi.o gaudi/gaudi_hwmgr.o gaudi/gaudi_security.o \ gaudi/gaudi_coresight.o diff --git a/drivers/misc/habanalabs/gaudi/gaudiP.h b/drivers/misc/habanalabs/gaudi/gaudiP.h index bdc5f96085a7..a94ab6a180f0 100644 --- a/drivers/misc/habanalabs/gaudi/gaudiP.h +++ b/drivers/misc/habanalabs/gaudi/gaudiP.h @@ -10,7 +10,7 @@ #include #include "habanalabs.h" -#include "include/hl_boot_if.h" +#include "include/common/hl_boot_if.h" #include "include/gaudi/gaudi_packets.h" #include "include/gaudi/gaudi.h" #include "include/gaudi/gaudi_async_events.h" diff --git a/drivers/misc/habanalabs/goya/goyaP.h b/drivers/misc/habanalabs/goya/goyaP.h index 8265cc21b45a..9e674cf39fd9 100644 --- a/drivers/misc/habanalabs/goya/goyaP.h +++ b/drivers/misc/habanalabs/goya/goyaP.h @@ -10,7 +10,7 @@ #include #include "habanalabs.h" -#include "include/hl_boot_if.h" +#include "include/common/hl_boot_if.h" #include "include/goya/goya_packets.h" #include "include/goya/goya.h" #include "include/goya/goya_async_events.h" diff --git a/drivers/misc/habanalabs/habanalabs.h b/drivers/misc/habanalabs/habanalabs.h deleted file mode 100644 index 14def0d26d2d..000000000000 --- a/drivers/misc/habanalabs/habanalabs.h +++ /dev/null @@ -1,1948 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 - * - * Copyright 2016-2019 HabanaLabs, Ltd. - * All Rights Reserved. - * - */ - -#ifndef HABANALABSP_H_ -#define HABANALABSP_H_ - -#include "include/armcp_if.h" -#include "include/qman_if.h" -#include - -#include -#include -#include -#include -#include -#include -#include - -#define HL_NAME "habanalabs" - -#define HL_MMAP_CB_MASK (0x8000000000000000ull >> PAGE_SHIFT) - -#define HL_PENDING_RESET_PER_SEC 30 - -#define HL_HARD_RESET_MAX_TIMEOUT 120 - -#define HL_DEVICE_TIMEOUT_USEC 1000000 /* 1 s */ - -#define HL_HEARTBEAT_PER_USEC 5000000 /* 5 s */ - -#define HL_PLL_LOW_JOB_FREQ_USEC 5000000 /* 5 s */ - -#define HL_ARMCP_INFO_TIMEOUT_USEC 10000000 /* 10s */ -#define HL_ARMCP_EEPROM_TIMEOUT_USEC 10000000 /* 10s */ - -#define HL_PCI_ELBI_TIMEOUT_MSEC 10 /* 10ms */ - -#define HL_SIM_MAX_TIMEOUT_US 10000000 /* 10s */ - -#define HL_IDLE_BUSY_TS_ARR_SIZE 4096 - -/* Memory */ -#define MEM_HASH_TABLE_BITS 7 /* 1 << 7 buckets */ - -/* MMU */ -#define MMU_HASH_TABLE_BITS 7 /* 1 << 7 buckets */ - -/* - * HL_RSVD_SOBS 'sync stream' reserved sync objects per QMAN stream - * HL_RSVD_MONS 'sync stream' reserved monitors per QMAN stream - */ -#define HL_RSVD_SOBS 4 -#define HL_RSVD_MONS 2 - -#define HL_RSVD_SOBS_IN_USE 2 -#define HL_RSVD_MONS_IN_USE 1 - -#define HL_MAX_SOB_VAL (1 << 15) - -#define IS_POWER_OF_2(n) (n != 0 && ((n & (n - 1)) == 0)) -#define IS_MAX_PENDING_CS_VALID(n) (IS_POWER_OF_2(n) && (n > 1)) - -#define HL_PCI_NUM_BARS 6 - -/** - * struct pgt_info - MMU hop page info. - * @node: hash linked-list node for the pgts shadow hash of pgts. - * @phys_addr: physical address of the pgt. - * @shadow_addr: shadow hop in the host. - * @ctx: pointer to the owner ctx. - * @num_of_ptes: indicates how many ptes are used in the pgt. - * - * The MMU page tables hierarchy is placed on the DRAM. When a new level (hop) - * is needed during mapping, a new page is allocated and this structure holds - * its essential information. During unmapping, if no valid PTEs remained in the - * page, it is freed with its pgt_info structure. - */ -struct pgt_info { - struct hlist_node node; - u64 phys_addr; - u64 shadow_addr; - struct hl_ctx *ctx; - int num_of_ptes; -}; - -struct hl_device; -struct hl_fpriv; - -/** - * enum hl_pci_match_mode - pci match mode per region - * @PCI_ADDRESS_MATCH_MODE: address match mode - * @PCI_BAR_MATCH_MODE: bar match mode - */ -enum hl_pci_match_mode { - PCI_ADDRESS_MATCH_MODE, - PCI_BAR_MATCH_MODE -}; - -/** - * enum hl_fw_component - F/W components to read version through registers. - * @FW_COMP_UBOOT: u-boot. - * @FW_COMP_PREBOOT: preboot. - */ -enum hl_fw_component { - FW_COMP_UBOOT, - FW_COMP_PREBOOT -}; - -/** - * enum hl_queue_type - Supported QUEUE types. - * @QUEUE_TYPE_NA: queue is not available. - * @QUEUE_TYPE_EXT: external queue which is a DMA channel that may access the - * host. - * @QUEUE_TYPE_INT: internal queue that performs DMA inside the device's - * memories and/or operates the compute engines. - * @QUEUE_TYPE_CPU: S/W queue for communication with the device's CPU. - * @QUEUE_TYPE_HW: queue of DMA and compute engines jobs, for which completion - * notifications are sent by H/W. - */ -enum hl_queue_type { - QUEUE_TYPE_NA, - QUEUE_TYPE_EXT, - QUEUE_TYPE_INT, - QUEUE_TYPE_CPU, - QUEUE_TYPE_HW -}; - -enum hl_cs_type { - CS_TYPE_DEFAULT, - CS_TYPE_SIGNAL, - CS_TYPE_WAIT -}; - -/* - * struct hl_inbound_pci_region - inbound region descriptor - * @mode: pci match mode for this region - * @addr: region target address - * @size: region size in bytes - * @offset_in_bar: offset within bar (address match mode) - * @bar: bar id - */ -struct hl_inbound_pci_region { - enum hl_pci_match_mode mode; - u64 addr; - u64 size; - u64 offset_in_bar; - u8 bar; -}; - -/* - * struct hl_outbound_pci_region - outbound region descriptor - * @addr: region target address - * @size: region size in bytes - */ -struct hl_outbound_pci_region { - u64 addr; - u64 size; -}; - -/* - * struct hl_hw_sob - H/W SOB info. - * @hdev: habanalabs device structure. - * @kref: refcount of this SOB. The SOB will reset once the refcount is zero. - * @sob_id: id of this SOB. - * @q_idx: the H/W queue that uses this SOB. - */ -struct hl_hw_sob { - struct hl_device *hdev; - struct kref kref; - u32 sob_id; - u32 q_idx; -}; - -/** - * struct hw_queue_properties - queue information. - * @type: queue type. - * @driver_only: true if only the driver is allowed to send a job to this queue, - * false otherwise. - * @requires_kernel_cb: true if a CB handle must be provided for jobs on this - * queue, false otherwise (a CB address must be provided). - * @supports_sync_stream: True if queue supports sync stream - */ -struct hw_queue_properties { - enum hl_queue_type type; - u8 driver_only; - u8 requires_kernel_cb; - u8 supports_sync_stream; -}; - -/** - * enum vm_type_t - virtual memory mapping request information. - * @VM_TYPE_USERPTR: mapping of user memory to device virtual address. - * @VM_TYPE_PHYS_PACK: mapping of DRAM memory to device virtual address. - */ -enum vm_type_t { - VM_TYPE_USERPTR = 0x1, - VM_TYPE_PHYS_PACK = 0x2 -}; - -/** - * enum hl_device_hw_state - H/W device state. use this to understand whether - * to do reset before hw_init or not - * @HL_DEVICE_HW_STATE_CLEAN: H/W state is clean. i.e. after hard reset - * @HL_DEVICE_HW_STATE_DIRTY: H/W state is dirty. i.e. we started to execute - * hw_init - */ -enum hl_device_hw_state { - HL_DEVICE_HW_STATE_CLEAN = 0, - HL_DEVICE_HW_STATE_DIRTY -}; - -/** - * struct hl_mmu_properties - ASIC specific MMU address translation properties. - * @start_addr: virtual start address of the memory region. - * @end_addr: virtual end address of the memory region. - * @hop0_shift: shift of hop 0 mask. - * @hop1_shift: shift of hop 1 mask. - * @hop2_shift: shift of hop 2 mask. - * @hop3_shift: shift of hop 3 mask. - * @hop4_shift: shift of hop 4 mask. - * @hop0_mask: mask to get the PTE address in hop 0. - * @hop1_mask: mask to get the PTE address in hop 1. - * @hop2_mask: mask to get the PTE address in hop 2. - * @hop3_mask: mask to get the PTE address in hop 3. - * @hop4_mask: mask to get the PTE address in hop 4. - * @page_size: default page size used to allocate memory. - */ -struct hl_mmu_properties { - u64 start_addr; - u64 end_addr; - u64 hop0_shift; - u64 hop1_shift; - u64 hop2_shift; - u64 hop3_shift; - u64 hop4_shift; - u64 hop0_mask; - u64 hop1_mask; - u64 hop2_mask; - u64 hop3_mask; - u64 hop4_mask; - u32 page_size; -}; - -/** - * struct asic_fixed_properties - ASIC specific immutable properties. - * @hw_queues_props: H/W queues properties. - * @armcp_info: received various information from ArmCP regarding the H/W, e.g. - * available sensors. - * @uboot_ver: F/W U-boot version. - * @preboot_ver: F/W Preboot version. - * @dmmu: DRAM MMU address translation properties. - * @pmmu: PCI (host) MMU address translation properties. - * @pmmu_huge: PCI (host) MMU address translation properties for memory - * allocated with huge pages. - * @sram_base_address: SRAM physical start address. - * @sram_end_address: SRAM physical end address. - * @sram_user_base_address - SRAM physical start address for user access. - * @dram_base_address: DRAM physical start address. - * @dram_end_address: DRAM physical end address. - * @dram_user_base_address: DRAM physical start address for user access. - * @dram_size: DRAM total size. - * @dram_pci_bar_size: size of PCI bar towards DRAM. - * @max_power_default: max power of the device after reset - * @dram_size_for_default_page_mapping: DRAM size needed to map to avoid page - * fault. - * @pcie_dbi_base_address: Base address of the PCIE_DBI block. - * @pcie_aux_dbi_reg_addr: Address of the PCIE_AUX DBI register. - * @mmu_pgt_addr: base physical address in DRAM of MMU page tables. - * @mmu_dram_default_page_addr: DRAM default page physical address. - * @mmu_pgt_size: MMU page tables total size. - * @mmu_pte_size: PTE size in MMU page tables. - * @mmu_hop_table_size: MMU hop table size. - * @mmu_hop0_tables_total_size: total size of MMU hop0 tables. - * @dram_page_size: page size for MMU DRAM allocation. - * @cfg_size: configuration space size on SRAM. - * @sram_size: total size of SRAM. - * @max_asid: maximum number of open contexts (ASIDs). - * @num_of_events: number of possible internal H/W IRQs. - * @psoc_pci_pll_nr: PCI PLL NR value. - * @psoc_pci_pll_nf: PCI PLL NF value. - * @psoc_pci_pll_od: PCI PLL OD value. - * @psoc_pci_pll_div_factor: PCI PLL DIV FACTOR 1 value. - * @psoc_timestamp_frequency: frequency of the psoc timestamp clock. - * @high_pll: high PLL frequency used by the device. - * @cb_pool_cb_cnt: number of CBs in the CB pool. - * @cb_pool_cb_size: size of each CB in the CB pool. - * @max_pending_cs: maximum of concurrent pending command submissions - * @max_queues: maximum amount of queues in the system - * @sync_stream_first_sob: first sync object available for sync stream use - * @sync_stream_first_mon: first monitor available for sync stream use - * @tpc_enabled_mask: which TPCs are enabled. - * @completion_queues_count: number of completion queues. - */ -struct asic_fixed_properties { - struct hw_queue_properties *hw_queues_props; - struct armcp_info armcp_info; - char uboot_ver[VERSION_MAX_LEN]; - char preboot_ver[VERSION_MAX_LEN]; - struct hl_mmu_properties dmmu; - struct hl_mmu_properties pmmu; - struct hl_mmu_properties pmmu_huge; - u64 sram_base_address; - u64 sram_end_address; - u64 sram_user_base_address; - u64 dram_base_address; - u64 dram_end_address; - u64 dram_user_base_address; - u64 dram_size; - u64 dram_pci_bar_size; - u64 max_power_default; - u64 dram_size_for_default_page_mapping; - u64 pcie_dbi_base_address; - u64 pcie_aux_dbi_reg_addr; - u64 mmu_pgt_addr; - u64 mmu_dram_default_page_addr; - u32 mmu_pgt_size; - u32 mmu_pte_size; - u32 mmu_hop_table_size; - u32 mmu_hop0_tables_total_size; - u32 dram_page_size; - u32 cfg_size; - u32 sram_size; - u32 max_asid; - u32 num_of_events; - u32 psoc_pci_pll_nr; - u32 psoc_pci_pll_nf; - u32 psoc_pci_pll_od; - u32 psoc_pci_pll_div_factor; - u32 psoc_timestamp_frequency; - u32 high_pll; - u32 cb_pool_cb_cnt; - u32 cb_pool_cb_size; - u32 max_pending_cs; - u32 max_queues; - u16 sync_stream_first_sob; - u16 sync_stream_first_mon; - u8 tpc_enabled_mask; - u8 completion_queues_count; -}; - -/** - * struct hl_cs_compl - command submission completion object. - * @base_fence: kernel fence object. - * @lock: spinlock to protect fence. - * @hdev: habanalabs device structure. - * @hw_sob: the H/W SOB used in this signal/wait CS. - * @cs_seq: command submission sequence number. - * @type: type of the CS - signal/wait. - * @sob_val: the SOB value that is used in this signal/wait CS. - */ -struct hl_cs_compl { - struct dma_fence base_fence; - spinlock_t lock; - struct hl_device *hdev; - struct hl_hw_sob *hw_sob; - u64 cs_seq; - enum hl_cs_type type; - u16 sob_val; -}; - -/* - * Command Buffers - */ - -/** - * struct hl_cb_mgr - describes a Command Buffer Manager. - * @cb_lock: protects cb_handles. - * @cb_handles: an idr to hold all command buffer handles. - */ -struct hl_cb_mgr { - spinlock_t cb_lock; - struct idr cb_handles; /* protected by cb_lock */ -}; - -/** - * struct hl_cb - describes a Command Buffer. - * @refcount: reference counter for usage of the CB. - * @hdev: pointer to device this CB belongs to. - * @lock: spinlock to protect mmap/cs flows. - * @debugfs_list: node in debugfs list of command buffers. - * @pool_list: node in pool list of command buffers. - * @kernel_address: Holds the CB's kernel virtual address. - * @bus_address: Holds the CB's DMA address. - * @mmap_size: Holds the CB's size that was mmaped. - * @size: holds the CB's size. - * @id: the CB's ID. - * @cs_cnt: holds number of CS that this CB participates in. - * @ctx_id: holds the ID of the owner's context. - * @mmap: true if the CB is currently mmaped to user. - * @is_pool: true if CB was acquired from the pool, false otherwise. - */ -struct hl_cb { - struct kref refcount; - struct hl_device *hdev; - spinlock_t lock; - struct list_head debugfs_list; - struct list_head pool_list; - u64 kernel_address; - dma_addr_t bus_address; - u32 mmap_size; - u32 size; - u32 id; - u32 cs_cnt; - u32 ctx_id; - u8 mmap; - u8 is_pool; -}; - - -/* - * QUEUES - */ - -struct hl_cs_job; - -/* Queue length of external and HW queues */ -#define HL_QUEUE_LENGTH 4096 -#define HL_QUEUE_SIZE_IN_BYTES (HL_QUEUE_LENGTH * HL_BD_SIZE) - -#if (HL_MAX_JOBS_PER_CS > HL_QUEUE_LENGTH) -#error "HL_QUEUE_LENGTH must be greater than HL_MAX_JOBS_PER_CS" -#endif - -/* HL_CQ_LENGTH is in units of struct hl_cq_entry */ -#define HL_CQ_LENGTH HL_QUEUE_LENGTH -#define HL_CQ_SIZE_IN_BYTES (HL_CQ_LENGTH * HL_CQ_ENTRY_SIZE) - -/* Must be power of 2 */ -#define HL_EQ_LENGTH 64 -#define HL_EQ_SIZE_IN_BYTES (HL_EQ_LENGTH * HL_EQ_ENTRY_SIZE) - -/* Host <-> ArmCP shared memory size */ -#define HL_CPU_ACCESSIBLE_MEM_SIZE SZ_2M - -/** - * struct hl_hw_queue - describes a H/W transport queue. - * @hw_sob: array of the used H/W SOBs by this H/W queue. - * @shadow_queue: pointer to a shadow queue that holds pointers to jobs. - * @queue_type: type of queue. - * @kernel_address: holds the queue's kernel virtual address. - * @bus_address: holds the queue's DMA address. - * @pi: holds the queue's pi value. - * @ci: holds the queue's ci value, AS CALCULATED BY THE DRIVER (not real ci). - * @hw_queue_id: the id of the H/W queue. - * @cq_id: the id for the corresponding CQ for this H/W queue. - * @msi_vec: the IRQ number of the H/W queue. - * @int_queue_len: length of internal queue (number of entries). - * @next_sob_val: the next value to use for the currently used SOB. - * @base_sob_id: the base SOB id of the SOBs used by this queue. - * @base_mon_id: the base MON id of the MONs used by this queue. - * @valid: is the queue valid (we have array of 32 queues, not all of them - * exist). - * @curr_sob_offset: the id offset to the currently used SOB from the - * HL_RSVD_SOBS that are being used by this queue. - * @supports_sync_stream: True if queue supports sync stream - */ -struct hl_hw_queue { - struct hl_hw_sob hw_sob[HL_RSVD_SOBS]; - struct hl_cs_job **shadow_queue; - enum hl_queue_type queue_type; - u64 kernel_address; - dma_addr_t bus_address; - u32 pi; - atomic_t ci; - u32 hw_queue_id; - u32 cq_id; - u32 msi_vec; - u16 int_queue_len; - u16 next_sob_val; - u16 base_sob_id; - u16 base_mon_id; - u8 valid; - u8 curr_sob_offset; - u8 supports_sync_stream; -}; - -/** - * struct hl_cq - describes a completion queue - * @hdev: pointer to the device structure - * @kernel_address: holds the queue's kernel virtual address - * @bus_address: holds the queue's DMA address - * @cq_idx: completion queue index in array - * @hw_queue_id: the id of the matching H/W queue - * @ci: ci inside the queue - * @pi: pi inside the queue - * @free_slots_cnt: counter of free slots in queue - */ -struct hl_cq { - struct hl_device *hdev; - u64 kernel_address; - dma_addr_t bus_address; - u32 cq_idx; - u32 hw_queue_id; - u32 ci; - u32 pi; - atomic_t free_slots_cnt; -}; - -/** - * struct hl_eq - describes the event queue (single one per device) - * @hdev: pointer to the device structure - * @kernel_address: holds the queue's kernel virtual address - * @bus_address: holds the queue's DMA address - * @ci: ci inside the queue - */ -struct hl_eq { - struct hl_device *hdev; - u64 kernel_address; - dma_addr_t bus_address; - u32 ci; -}; - - -/* - * ASICs - */ - -/** - * enum hl_asic_type - supported ASIC types. - * @ASIC_INVALID: Invalid ASIC type. - * @ASIC_GOYA: Goya device. - * @ASIC_GAUDI: Gaudi device. - */ -enum hl_asic_type { - ASIC_INVALID, - ASIC_GOYA, - ASIC_GAUDI -}; - -struct hl_cs_parser; - -/** - * enum hl_pm_mng_profile - power management profile. - * @PM_AUTO: internal clock is set by the Linux driver. - * @PM_MANUAL: internal clock is set by the user. - * @PM_LAST: last power management type. - */ -enum hl_pm_mng_profile { - PM_AUTO = 1, - PM_MANUAL, - PM_LAST -}; - -/** - * enum hl_pll_frequency - PLL frequency. - * @PLL_HIGH: high frequency. - * @PLL_LOW: low frequency. - * @PLL_LAST: last frequency values that were configured by the user. - */ -enum hl_pll_frequency { - PLL_HIGH = 1, - PLL_LOW, - PLL_LAST -}; - -#define PLL_REF_CLK 50 - -enum div_select_defs { - DIV_SEL_REF_CLK = 0, - DIV_SEL_PLL_CLK = 1, - DIV_SEL_DIVIDED_REF = 2, - DIV_SEL_DIVIDED_PLL = 3, -}; - -/** - * struct hl_asic_funcs - ASIC specific functions that are can be called from - * common code. - * @early_init: sets up early driver state (pre sw_init), doesn't configure H/W. - * @early_fini: tears down what was done in early_init. - * @late_init: sets up late driver/hw state (post hw_init) - Optional. - * @late_fini: tears down what was done in late_init (pre hw_fini) - Optional. - * @sw_init: sets up driver state, does not configure H/W. - * @sw_fini: tears down driver state, does not configure H/W. - * @hw_init: sets up the H/W state. - * @hw_fini: tears down the H/W state. - * @halt_engines: halt engines, needed for reset sequence. This also disables - * interrupts from the device. Should be called before - * hw_fini and before CS rollback. - * @suspend: handles IP specific H/W or SW changes for suspend. - * @resume: handles IP specific H/W or SW changes for resume. - * @cb_mmap: maps a CB. - * @ring_doorbell: increment PI on a given QMAN. - * @pqe_write: Write the PQ entry to the PQ. This is ASIC-specific - * function because the PQs are located in different memory areas - * per ASIC (SRAM, DRAM, Host memory) and therefore, the method of - * writing the PQE must match the destination memory area - * properties. - * @asic_dma_alloc_coherent: Allocate coherent DMA memory by calling - * dma_alloc_coherent(). This is ASIC function because - * its implementation is not trivial when the driver - * is loaded in simulation mode (not upstreamed). - * @asic_dma_free_coherent: Free coherent DMA memory by calling - * dma_free_coherent(). This is ASIC function because - * its implementation is not trivial when the driver - * is loaded in simulation mode (not upstreamed). - * @get_int_queue_base: get the internal queue base address. - * @test_queues: run simple test on all queues for sanity check. - * @asic_dma_pool_zalloc: small DMA allocation of coherent memory from DMA pool. - * size of allocation is HL_DMA_POOL_BLK_SIZE. - * @asic_dma_pool_free: free small DMA allocation from pool. - * @cpu_accessible_dma_pool_alloc: allocate CPU PQ packet from DMA pool. - * @cpu_accessible_dma_pool_free: free CPU PQ packet from DMA pool. - * @hl_dma_unmap_sg: DMA unmap scatter-gather list. - * @cs_parser: parse Command Submission. - * @asic_dma_map_sg: DMA map scatter-gather list. - * @get_dma_desc_list_size: get number of LIN_DMA packets required for CB. - * @add_end_of_cb_packets: Add packets to the end of CB, if device requires it. - * @update_eq_ci: update event queue CI. - * @context_switch: called upon ASID context switch. - * @restore_phase_topology: clear all SOBs amd MONs. - * @debugfs_read32: debug interface for reading u32 from DRAM/SRAM. - * @debugfs_write32: debug interface for writing u32 to DRAM/SRAM. - * @add_device_attr: add ASIC specific device attributes. - * @handle_eqe: handle event queue entry (IRQ) from ArmCP. - * @set_pll_profile: change PLL profile (manual/automatic). - * @get_events_stat: retrieve event queue entries histogram. - * @read_pte: read MMU page table entry from DRAM. - * @write_pte: write MMU page table entry to DRAM. - * @mmu_invalidate_cache: flush MMU STLB host/DRAM cache, either with soft - * (L1 only) or hard (L0 & L1) flush. - * @mmu_invalidate_cache_range: flush specific MMU STLB cache lines with - * ASID-VA-size mask. - * @send_heartbeat: send is-alive packet to ArmCP and verify response. - * @enable_clock_gating: enable clock gating for reducing power consumption. - * @disable_clock_gating: disable clock for accessing registers on HBW. - * @debug_coresight: perform certain actions on Coresight for debugging. - * @is_device_idle: return true if device is idle, false otherwise. - * @soft_reset_late_init: perform certain actions needed after soft reset. - * @hw_queues_lock: acquire H/W queues lock. - * @hw_queues_unlock: release H/W queues lock. - * @get_pci_id: retrieve PCI ID. - * @get_eeprom_data: retrieve EEPROM data from F/W. - * @send_cpu_message: send buffer to ArmCP. - * @get_hw_state: retrieve the H/W state - * @pci_bars_map: Map PCI BARs. - * @set_dram_bar_base: Set DRAM BAR to map specific device address. Returns - * old address the bar pointed to or U64_MAX for failure - * @init_iatu: Initialize the iATU unit inside the PCI controller. - * @rreg: Read a register. Needed for simulator support. - * @wreg: Write a register. Needed for simulator support. - * @halt_coresight: stop the ETF and ETR traces. - * @get_clk_rate: Retrieve the ASIC current and maximum clock rate in MHz - * @get_queue_id_for_cq: Get the H/W queue id related to the given CQ index. - * @read_device_fw_version: read the device's firmware versions that are - * contained in registers - * @load_firmware_to_device: load the firmware to the device's memory - * @load_boot_fit_to_device: load boot fit to device's memory - * @get_signal_cb_size: Get signal CB size. - * @get_wait_cb_size: Get wait CB size. - * @gen_signal_cb: Generate a signal CB. - * @gen_wait_cb: Generate a wait CB. - * @reset_sob: Reset a SOB. - * @set_dma_mask_from_fw: set the DMA mask in the driver according to the - * firmware configuration - * @get_device_time: Get the device time. - */ -struct hl_asic_funcs { - int (*early_init)(struct hl_device *hdev); - int (*early_fini)(struct hl_device *hdev); - int (*late_init)(struct hl_device *hdev); - void (*late_fini)(struct hl_device *hdev); - int (*sw_init)(struct hl_device *hdev); - int (*sw_fini)(struct hl_device *hdev); - int (*hw_init)(struct hl_device *hdev); - void (*hw_fini)(struct hl_device *hdev, bool hard_reset); - void (*halt_engines)(struct hl_device *hdev, bool hard_reset); - int (*suspend)(struct hl_device *hdev); - int (*resume)(struct hl_device *hdev); - int (*cb_mmap)(struct hl_device *hdev, struct vm_area_struct *vma, - u64 kaddress, phys_addr_t paddress, u32 size); - void (*ring_doorbell)(struct hl_device *hdev, u32 hw_queue_id, u32 pi); - void (*pqe_write)(struct hl_device *hdev, __le64 *pqe, - struct hl_bd *bd); - void* (*asic_dma_alloc_coherent)(struct hl_device *hdev, size_t size, - dma_addr_t *dma_handle, gfp_t flag); - void (*asic_dma_free_coherent)(struct hl_device *hdev, size_t size, - void *cpu_addr, dma_addr_t dma_handle); - void* (*get_int_queue_base)(struct hl_device *hdev, u32 queue_id, - dma_addr_t *dma_handle, u16 *queue_len); - int (*test_queues)(struct hl_device *hdev); - void* (*asic_dma_pool_zalloc)(struct hl_device *hdev, size_t size, - gfp_t mem_flags, dma_addr_t *dma_handle); - void (*asic_dma_pool_free)(struct hl_device *hdev, void *vaddr, - dma_addr_t dma_addr); - void* (*cpu_accessible_dma_pool_alloc)(struct hl_device *hdev, - size_t size, dma_addr_t *dma_handle); - void (*cpu_accessible_dma_pool_free)(struct hl_device *hdev, - size_t size, void *vaddr); - void (*hl_dma_unmap_sg)(struct hl_device *hdev, - struct scatterlist *sgl, int nents, - enum dma_data_direction dir); - int (*cs_parser)(struct hl_device *hdev, struct hl_cs_parser *parser); - int (*asic_dma_map_sg)(struct hl_device *hdev, - struct scatterlist *sgl, int nents, - enum dma_data_direction dir); - u32 (*get_dma_desc_list_size)(struct hl_device *hdev, - struct sg_table *sgt); - void (*add_end_of_cb_packets)(struct hl_device *hdev, - u64 kernel_address, u32 len, - u64 cq_addr, u32 cq_val, u32 msix_num, - bool eb); - void (*update_eq_ci)(struct hl_device *hdev, u32 val); - int (*context_switch)(struct hl_device *hdev, u32 asid); - void (*restore_phase_topology)(struct hl_device *hdev); - int (*debugfs_read32)(struct hl_device *hdev, u64 addr, u32 *val); - int (*debugfs_write32)(struct hl_device *hdev, u64 addr, u32 val); - int (*debugfs_read64)(struct hl_device *hdev, u64 addr, u64 *val); - int (*debugfs_write64)(struct hl_device *hdev, u64 addr, u64 val); - void (*add_device_attr)(struct hl_device *hdev, - struct attribute_group *dev_attr_grp); - void (*handle_eqe)(struct hl_device *hdev, - struct hl_eq_entry *eq_entry); - void (*set_pll_profile)(struct hl_device *hdev, - enum hl_pll_frequency freq); - void* (*get_events_stat)(struct hl_device *hdev, bool aggregate, - u32 *size); - u64 (*read_pte)(struct hl_device *hdev, u64 addr); - void (*write_pte)(struct hl_device *hdev, u64 addr, u64 val); - int (*mmu_invalidate_cache)(struct hl_device *hdev, bool is_hard, - u32 flags); - int (*mmu_invalidate_cache_range)(struct hl_device *hdev, bool is_hard, - u32 asid, u64 va, u64 size); - int (*send_heartbeat)(struct hl_device *hdev); - void (*enable_clock_gating)(struct hl_device *hdev); - void (*disable_clock_gating)(struct hl_device *hdev); - int (*debug_coresight)(struct hl_device *hdev, void *data); - bool (*is_device_idle)(struct hl_device *hdev, u32 *mask, - struct seq_file *s); - int (*soft_reset_late_init)(struct hl_device *hdev); - void (*hw_queues_lock)(struct hl_device *hdev); - void (*hw_queues_unlock)(struct hl_device *hdev); - u32 (*get_pci_id)(struct hl_device *hdev); - int (*get_eeprom_data)(struct hl_device *hdev, void *data, - size_t max_size); - int (*send_cpu_message)(struct hl_device *hdev, u32 *msg, - u16 len, u32 timeout, long *result); - enum hl_device_hw_state (*get_hw_state)(struct hl_device *hdev); - int (*pci_bars_map)(struct hl_device *hdev); - u64 (*set_dram_bar_base)(struct hl_device *hdev, u64 addr); - int (*init_iatu)(struct hl_device *hdev); - u32 (*rreg)(struct hl_device *hdev, u32 reg); - void (*wreg)(struct hl_device *hdev, u32 reg, u32 val); - void (*halt_coresight)(struct hl_device *hdev); - int (*get_clk_rate)(struct hl_device *hdev, u32 *cur_clk, u32 *max_clk); - u32 (*get_queue_id_for_cq)(struct hl_device *hdev, u32 cq_idx); - void (*read_device_fw_version)(struct hl_device *hdev, - enum hl_fw_component fwc); - int (*load_firmware_to_device)(struct hl_device *hdev); - int (*load_boot_fit_to_device)(struct hl_device *hdev); - u32 (*get_signal_cb_size)(struct hl_device *hdev); - u32 (*get_wait_cb_size)(struct hl_device *hdev); - void (*gen_signal_cb)(struct hl_device *hdev, void *data, u16 sob_id); - void (*gen_wait_cb)(struct hl_device *hdev, void *data, u16 sob_id, - u16 sob_val, u16 mon_id, u32 q_idx); - void (*reset_sob)(struct hl_device *hdev, void *data); - void (*set_dma_mask_from_fw)(struct hl_device *hdev); - u64 (*get_device_time)(struct hl_device *hdev); -}; - - -/* - * CONTEXTS - */ - -#define HL_KERNEL_ASID_ID 0 - -/** - * struct hl_va_range - virtual addresses range. - * @lock: protects the virtual addresses list. - * @list: list of virtual addresses blocks available for mappings. - * @start_addr: range start address. - * @end_addr: range end address. - */ -struct hl_va_range { - struct mutex lock; - struct list_head list; - u64 start_addr; - u64 end_addr; -}; - -/** - * struct hl_ctx - user/kernel context. - * @mem_hash: holds mapping from virtual address to virtual memory area - * descriptor (hl_vm_phys_pg_list or hl_userptr). - * @mmu_shadow_hash: holds a mapping from shadow address to pgt_info structure. - * @hpriv: pointer to the private (Kernel Driver) data of the process (fd). - * @hdev: pointer to the device structure. - * @refcount: reference counter for the context. Context is released only when - * this hits 0l. It is incremented on CS and CS_WAIT. - * @cs_pending: array of DMA fence objects representing pending CS. - * @host_va_range: holds available virtual addresses for host mappings. - * @host_huge_va_range: holds available virtual addresses for host mappings - * with huge pages. - * @dram_va_range: holds available virtual addresses for DRAM mappings. - * @mem_hash_lock: protects the mem_hash. - * @mmu_lock: protects the MMU page tables. Any change to the PGT, modifying the - * MMU hash or walking the PGT requires talking this lock. - * @debugfs_list: node in debugfs list of contexts. - * @cs_sequence: sequence number for CS. Value is assigned to a CS and passed - * to user so user could inquire about CS. It is used as - * index to cs_pending array. - * @dram_default_hops: array that holds all hops addresses needed for default - * DRAM mapping. - * @cs_lock: spinlock to protect cs_sequence. - * @dram_phys_mem: amount of used physical DRAM memory by this context. - * @thread_ctx_switch_token: token to prevent multiple threads of the same - * context from running the context switch phase. - * Only a single thread should run it. - * @thread_ctx_switch_wait_token: token to prevent the threads that didn't run - * the context switch phase from moving to their - * execution phase before the context switch phase - * has finished. - * @asid: context's unique address space ID in the device's MMU. - * @handle: context's opaque handle for user - */ -struct hl_ctx { - DECLARE_HASHTABLE(mem_hash, MEM_HASH_TABLE_BITS); - DECLARE_HASHTABLE(mmu_shadow_hash, MMU_HASH_TABLE_BITS); - struct hl_fpriv *hpriv; - struct hl_device *hdev; - struct kref refcount; - struct dma_fence **cs_pending; - struct hl_va_range *host_va_range; - struct hl_va_range *host_huge_va_range; - struct hl_va_range *dram_va_range; - struct mutex mem_hash_lock; - struct mutex mmu_lock; - struct list_head debugfs_list; - struct hl_cs_counters cs_counters; - u64 cs_sequence; - u64 *dram_default_hops; - spinlock_t cs_lock; - atomic64_t dram_phys_mem; - atomic_t thread_ctx_switch_token; - u32 thread_ctx_switch_wait_token; - u32 asid; - u32 handle; -}; - -/** - * struct hl_ctx_mgr - for handling multiple contexts. - * @ctx_lock: protects ctx_handles. - * @ctx_handles: idr to hold all ctx handles. - */ -struct hl_ctx_mgr { - struct mutex ctx_lock; - struct idr ctx_handles; -}; - - - -/* - * COMMAND SUBMISSIONS - */ - -/** - * struct hl_userptr - memory mapping chunk information - * @vm_type: type of the VM. - * @job_node: linked-list node for hanging the object on the Job's list. - * @vec: pointer to the frame vector. - * @sgt: pointer to the scatter-gather table that holds the pages. - * @dir: for DMA unmapping, the direction must be supplied, so save it. - * @debugfs_list: node in debugfs list of command submissions. - * @addr: user-space virtual address of the start of the memory area. - * @size: size of the memory area to pin & map. - * @dma_mapped: true if the SG was mapped to DMA addresses, false otherwise. - */ -struct hl_userptr { - enum vm_type_t vm_type; /* must be first */ - struct list_head job_node; - struct frame_vector *vec; - struct sg_table *sgt; - enum dma_data_direction dir; - struct list_head debugfs_list; - u64 addr; - u32 size; - u8 dma_mapped; -}; - -/** - * struct hl_cs - command submission. - * @jobs_in_queue_cnt: per each queue, maintain counter of submitted jobs. - * @ctx: the context this CS belongs to. - * @job_list: list of the CS's jobs in the various queues. - * @job_lock: spinlock for the CS's jobs list. Needed for free_job. - * @refcount: reference counter for usage of the CS. - * @fence: pointer to the fence object of this CS. - * @signal_fence: pointer to the fence object of the signal CS (used by wait - * CS only). - * @finish_work: workqueue object to run when CS is completed by H/W. - * @work_tdr: delayed work node for TDR. - * @mirror_node : node in device mirror list of command submissions. - * @debugfs_list: node in debugfs list of command submissions. - * @sequence: the sequence number of this CS. - * @type: CS_TYPE_*. - * @submitted: true if CS was submitted to H/W. - * @completed: true if CS was completed by device. - * @timedout : true if CS was timedout. - * @tdr_active: true if TDR was activated for this CS (to prevent - * double TDR activation). - * @aborted: true if CS was aborted due to some device error. - */ -struct hl_cs { - u16 *jobs_in_queue_cnt; - struct hl_ctx *ctx; - struct list_head job_list; - spinlock_t job_lock; - struct kref refcount; - struct dma_fence *fence; - struct dma_fence *signal_fence; - struct work_struct finish_work; - struct delayed_work work_tdr; - struct list_head mirror_node; - struct list_head debugfs_list; - u64 sequence; - enum hl_cs_type type; - u8 submitted; - u8 completed; - u8 timedout; - u8 tdr_active; - u8 aborted; -}; - -/** - * struct hl_cs_job - command submission job. - * @cs_node: the node to hang on the CS jobs list. - * @cs: the CS this job belongs to. - * @user_cb: the CB we got from the user. - * @patched_cb: in case of patching, this is internal CB which is submitted on - * the queue instead of the CB we got from the IOCTL. - * @finish_work: workqueue object to run when job is completed. - * @userptr_list: linked-list of userptr mappings that belong to this job and - * wait for completion. - * @debugfs_list: node in debugfs list of command submission jobs. - * @queue_type: the type of the H/W queue this job is submitted to. - * @id: the id of this job inside a CS. - * @hw_queue_id: the id of the H/W queue this job is submitted to. - * @user_cb_size: the actual size of the CB we got from the user. - * @job_cb_size: the actual size of the CB that we put on the queue. - * @is_kernel_allocated_cb: true if the CB handle we got from the user holds a - * handle to a kernel-allocated CB object, false - * otherwise (SRAM/DRAM/host address). - * @contains_dma_pkt: whether the JOB contains at least one DMA packet. This - * info is needed later, when adding the 2xMSG_PROT at the - * end of the JOB, to know which barriers to put in the - * MSG_PROT packets. Relevant only for GAUDI as GOYA doesn't - * have streams so the engine can't be busy by another - * stream. - */ -struct hl_cs_job { - struct list_head cs_node; - struct hl_cs *cs; - struct hl_cb *user_cb; - struct hl_cb *patched_cb; - struct work_struct finish_work; - struct list_head userptr_list; - struct list_head debugfs_list; - enum hl_queue_type queue_type; - u32 id; - u32 hw_queue_id; - u32 user_cb_size; - u32 job_cb_size; - u8 is_kernel_allocated_cb; - u8 contains_dma_pkt; -}; - -/** - * struct hl_cs_parser - command submission parser properties. - * @user_cb: the CB we got from the user. - * @patched_cb: in case of patching, this is internal CB which is submitted on - * the queue instead of the CB we got from the IOCTL. - * @job_userptr_list: linked-list of userptr mappings that belong to the related - * job and wait for completion. - * @cs_sequence: the sequence number of the related CS. - * @queue_type: the type of the H/W queue this job is submitted to. - * @ctx_id: the ID of the context the related CS belongs to. - * @hw_queue_id: the id of the H/W queue this job is submitted to. - * @user_cb_size: the actual size of the CB we got from the user. - * @patched_cb_size: the size of the CB after parsing. - * @job_id: the id of the related job inside the related CS. - * @is_kernel_allocated_cb: true if the CB handle we got from the user holds a - * handle to a kernel-allocated CB object, false - * otherwise (SRAM/DRAM/host address). - * @contains_dma_pkt: whether the JOB contains at least one DMA packet. This - * info is needed later, when adding the 2xMSG_PROT at the - * end of the JOB, to know which barriers to put in the - * MSG_PROT packets. Relevant only for GAUDI as GOYA doesn't - * have streams so the engine can't be busy by another - * stream. - */ -struct hl_cs_parser { - struct hl_cb *user_cb; - struct hl_cb *patched_cb; - struct list_head *job_userptr_list; - u64 cs_sequence; - enum hl_queue_type queue_type; - u32 ctx_id; - u32 hw_queue_id; - u32 user_cb_size; - u32 patched_cb_size; - u8 job_id; - u8 is_kernel_allocated_cb; - u8 contains_dma_pkt; -}; - - -/* - * MEMORY STRUCTURE - */ - -/** - * struct hl_vm_hash_node - hash element from virtual address to virtual - * memory area descriptor (hl_vm_phys_pg_list or - * hl_userptr). - * @node: node to hang on the hash table in context object. - * @vaddr: key virtual address. - * @ptr: value pointer (hl_vm_phys_pg_list or hl_userptr). - */ -struct hl_vm_hash_node { - struct hlist_node node; - u64 vaddr; - void *ptr; -}; - -/** - * struct hl_vm_phys_pg_pack - physical page pack. - * @vm_type: describes the type of the virtual area descriptor. - * @pages: the physical page array. - * @npages: num physical pages in the pack. - * @total_size: total size of all the pages in this list. - * @mapping_cnt: number of shared mappings. - * @asid: the context related to this list. - * @page_size: size of each page in the pack. - * @flags: HL_MEM_* flags related to this list. - * @handle: the provided handle related to this list. - * @offset: offset from the first page. - * @contiguous: is contiguous physical memory. - * @created_from_userptr: is product of host virtual address. - */ -struct hl_vm_phys_pg_pack { - enum vm_type_t vm_type; /* must be first */ - u64 *pages; - u64 npages; - u64 total_size; - atomic_t mapping_cnt; - u32 asid; - u32 page_size; - u32 flags; - u32 handle; - u32 offset; - u8 contiguous; - u8 created_from_userptr; -}; - -/** - * struct hl_vm_va_block - virtual range block information. - * @node: node to hang on the virtual range list in context object. - * @start: virtual range start address. - * @end: virtual range end address. - * @size: virtual range size. - */ -struct hl_vm_va_block { - struct list_head node; - u64 start; - u64 end; - u64 size; -}; - -/** - * struct hl_vm - virtual memory manager for MMU. - * @dram_pg_pool: pool for DRAM physical pages of 2MB. - * @dram_pg_pool_refcount: reference counter for the pool usage. - * @idr_lock: protects the phys_pg_list_handles. - * @phys_pg_pack_handles: idr to hold all device allocations handles. - * @init_done: whether initialization was done. We need this because VM - * initialization might be skipped during device initialization. - */ -struct hl_vm { - struct gen_pool *dram_pg_pool; - struct kref dram_pg_pool_refcount; - spinlock_t idr_lock; - struct idr phys_pg_pack_handles; - u8 init_done; -}; - - -/* - * DEBUG, PROFILING STRUCTURE - */ - -/** - * struct hl_debug_params - Coresight debug parameters. - * @input: pointer to component specific input parameters. - * @output: pointer to component specific output parameters. - * @output_size: size of output buffer. - * @reg_idx: relevant register ID. - * @op: component operation to execute. - * @enable: true if to enable component debugging, false otherwise. - */ -struct hl_debug_params { - void *input; - void *output; - u32 output_size; - u32 reg_idx; - u32 op; - bool enable; -}; - -/* - * FILE PRIVATE STRUCTURE - */ - -/** - * struct hl_fpriv - process information stored in FD private data. - * @hdev: habanalabs device structure. - * @filp: pointer to the given file structure. - * @taskpid: current process ID. - * @ctx: current executing context. TODO: remove for multiple ctx per process - * @ctx_mgr: context manager to handle multiple context for this FD. - * @cb_mgr: command buffer manager to handle multiple buffers for this FD. - * @debugfs_list: list of relevant ASIC debugfs. - * @dev_node: node in the device list of file private data - * @refcount: number of related contexts. - * @restore_phase_mutex: lock for context switch and restore phase. - * @is_control: true for control device, false otherwise - */ -struct hl_fpriv { - struct hl_device *hdev; - struct file *filp; - struct pid *taskpid; - struct hl_ctx *ctx; - struct hl_ctx_mgr ctx_mgr; - struct hl_cb_mgr cb_mgr; - struct list_head debugfs_list; - struct list_head dev_node; - struct kref refcount; - struct mutex restore_phase_mutex; - u8 is_control; -}; - - -/* - * DebugFS - */ - -/** - * struct hl_info_list - debugfs file ops. - * @name: file name. - * @show: function to output information. - * @write: function to write to the file. - */ -struct hl_info_list { - const char *name; - int (*show)(struct seq_file *s, void *data); - ssize_t (*write)(struct file *file, const char __user *buf, - size_t count, loff_t *f_pos); -}; - -/** - * struct hl_debugfs_entry - debugfs dentry wrapper. - * @dent: base debugfs entry structure. - * @info_ent: dentry realted ops. - * @dev_entry: ASIC specific debugfs manager. - */ -struct hl_debugfs_entry { - struct dentry *dent; - const struct hl_info_list *info_ent; - struct hl_dbg_device_entry *dev_entry; -}; - -/** - * struct hl_dbg_device_entry - ASIC specific debugfs manager. - * @root: root dentry. - * @hdev: habanalabs device structure. - * @entry_arr: array of available hl_debugfs_entry. - * @file_list: list of available debugfs files. - * @file_mutex: protects file_list. - * @cb_list: list of available CBs. - * @cb_spinlock: protects cb_list. - * @cs_list: list of available CSs. - * @cs_spinlock: protects cs_list. - * @cs_job_list: list of available CB jobs. - * @cs_job_spinlock: protects cs_job_list. - * @userptr_list: list of available userptrs (virtual memory chunk descriptor). - * @userptr_spinlock: protects userptr_list. - * @ctx_mem_hash_list: list of available contexts with MMU mappings. - * @ctx_mem_hash_spinlock: protects cb_list. - * @addr: next address to read/write from/to in read/write32. - * @mmu_addr: next virtual address to translate to physical address in mmu_show. - * @mmu_asid: ASID to use while translating in mmu_show. - * @i2c_bus: generic u8 debugfs file for bus value to use in i2c_data_read. - * @i2c_bus: generic u8 debugfs file for address value to use in i2c_data_read. - * @i2c_bus: generic u8 debugfs file for register value to use in i2c_data_read. - */ -struct hl_dbg_device_entry { - struct dentry *root; - struct hl_device *hdev; - struct hl_debugfs_entry *entry_arr; - struct list_head file_list; - struct mutex file_mutex; - struct list_head cb_list; - spinlock_t cb_spinlock; - struct list_head cs_list; - spinlock_t cs_spinlock; - struct list_head cs_job_list; - spinlock_t cs_job_spinlock; - struct list_head userptr_list; - spinlock_t userptr_spinlock; - struct list_head ctx_mem_hash_list; - spinlock_t ctx_mem_hash_spinlock; - u64 addr; - u64 mmu_addr; - u32 mmu_asid; - u8 i2c_bus; - u8 i2c_addr; - u8 i2c_reg; -}; - - -/* - * DEVICES - */ - -/* Theoretical limit only. A single host can only contain up to 4 or 8 PCIe - * x16 cards. In extreme cases, there are hosts that can accommodate 16 cards. - */ -#define HL_MAX_MINORS 256 - -/* - * Registers read & write functions. - */ - -u32 hl_rreg(struct hl_device *hdev, u32 reg); -void hl_wreg(struct hl_device *hdev, u32 reg, u32 val); - -#define RREG32(reg) hdev->asic_funcs->rreg(hdev, (reg)) -#define WREG32(reg, v) hdev->asic_funcs->wreg(hdev, (reg), (v)) -#define DREG32(reg) pr_info("REGISTER: " #reg " : 0x%08X\n", \ - hdev->asic_funcs->rreg(hdev, (reg))) - -#define WREG32_P(reg, val, mask) \ - do { \ - u32 tmp_ = RREG32(reg); \ - tmp_ &= (mask); \ - tmp_ |= ((val) & ~(mask)); \ - WREG32(reg, tmp_); \ - } while (0) -#define WREG32_AND(reg, and) WREG32_P(reg, 0, and) -#define WREG32_OR(reg, or) WREG32_P(reg, or, ~(or)) - -#define RMWREG32(reg, val, mask) \ - do { \ - u32 tmp_ = RREG32(reg); \ - tmp_ &= ~(mask); \ - tmp_ |= ((val) << __ffs(mask)); \ - WREG32(reg, tmp_); \ - } while (0) - -#define RREG32_MASK(reg, mask) ((RREG32(reg) & mask) >> __ffs(mask)) - -#define REG_FIELD_SHIFT(reg, field) reg##_##field##_SHIFT -#define REG_FIELD_MASK(reg, field) reg##_##field##_MASK -#define WREG32_FIELD(reg, offset, field, val) \ - WREG32(mm##reg + offset, (RREG32(mm##reg + offset) & \ - ~REG_FIELD_MASK(reg, field)) | \ - (val) << REG_FIELD_SHIFT(reg, field)) - -/* Timeout should be longer when working with simulator but cap the - * increased timeout to some maximum - */ -#define hl_poll_timeout(hdev, addr, val, cond, sleep_us, timeout_us) \ -({ \ - ktime_t __timeout; \ - if (hdev->pdev) \ - __timeout = ktime_add_us(ktime_get(), timeout_us); \ - else \ - __timeout = ktime_add_us(ktime_get(),\ - min((u64)(timeout_us * 10), \ - (u64) HL_SIM_MAX_TIMEOUT_US)); \ - might_sleep_if(sleep_us); \ - for (;;) { \ - (val) = RREG32(addr); \ - if (cond) \ - break; \ - if (timeout_us && ktime_compare(ktime_get(), __timeout) > 0) { \ - (val) = RREG32(addr); \ - break; \ - } \ - if (sleep_us) \ - usleep_range((sleep_us >> 2) + 1, sleep_us); \ - } \ - (cond) ? 0 : -ETIMEDOUT; \ -}) - -/* - * address in this macro points always to a memory location in the - * host's (server's) memory. That location is updated asynchronously - * either by the direct access of the device or by another core. - * - * To work both in LE and BE architectures, we need to distinguish between the - * two states (device or another core updates the memory location). Therefore, - * if mem_written_by_device is true, the host memory being polled will be - * updated directly by the device. If false, the host memory being polled will - * be updated by host CPU. Required so host knows whether or not the memory - * might need to be byte-swapped before returning value to caller. - */ -#define hl_poll_timeout_memory(hdev, addr, val, cond, sleep_us, timeout_us, \ - mem_written_by_device) \ -({ \ - ktime_t __timeout; \ - if (hdev->pdev) \ - __timeout = ktime_add_us(ktime_get(), timeout_us); \ - else \ - __timeout = ktime_add_us(ktime_get(),\ - min((u64)(timeout_us * 10), \ - (u64) HL_SIM_MAX_TIMEOUT_US)); \ - might_sleep_if(sleep_us); \ - for (;;) { \ - /* Verify we read updates done by other cores or by device */ \ - mb(); \ - (val) = *((u32 *) (uintptr_t) (addr)); \ - if (mem_written_by_device) \ - (val) = le32_to_cpu(*(__le32 *) &(val)); \ - if (cond) \ - break; \ - if (timeout_us && ktime_compare(ktime_get(), __timeout) > 0) { \ - (val) = *((u32 *) (uintptr_t) (addr)); \ - if (mem_written_by_device) \ - (val) = le32_to_cpu(*(__le32 *) &(val)); \ - break; \ - } \ - if (sleep_us) \ - usleep_range((sleep_us >> 2) + 1, sleep_us); \ - } \ - (cond) ? 0 : -ETIMEDOUT; \ -}) - -#define hl_poll_timeout_device_memory(hdev, addr, val, cond, sleep_us, \ - timeout_us) \ -({ \ - ktime_t __timeout; \ - if (hdev->pdev) \ - __timeout = ktime_add_us(ktime_get(), timeout_us); \ - else \ - __timeout = ktime_add_us(ktime_get(),\ - min((u64)(timeout_us * 10), \ - (u64) HL_SIM_MAX_TIMEOUT_US)); \ - might_sleep_if(sleep_us); \ - for (;;) { \ - (val) = readl(addr); \ - if (cond) \ - break; \ - if (timeout_us && ktime_compare(ktime_get(), __timeout) > 0) { \ - (val) = readl(addr); \ - break; \ - } \ - if (sleep_us) \ - usleep_range((sleep_us >> 2) + 1, sleep_us); \ - } \ - (cond) ? 0 : -ETIMEDOUT; \ -}) - -struct hwmon_chip_info; - -/** - * struct hl_device_reset_work - reset workqueue task wrapper. - * @reset_work: reset work to be done. - * @hdev: habanalabs device structure. - */ -struct hl_device_reset_work { - struct work_struct reset_work; - struct hl_device *hdev; -}; - -/** - * struct hl_device_idle_busy_ts - used for calculating device utilization rate. - * @idle_to_busy_ts: timestamp where device changed from idle to busy. - * @busy_to_idle_ts: timestamp where device changed from busy to idle. - */ -struct hl_device_idle_busy_ts { - ktime_t idle_to_busy_ts; - ktime_t busy_to_idle_ts; -}; - -/** - * struct hl_device - habanalabs device structure. - * @pdev: pointer to PCI device, can be NULL in case of simulator device. - * @pcie_bar_phys: array of available PCIe bars physical addresses. - * (required only for PCI address match mode) - * @pcie_bar: array of available PCIe bars virtual addresses. - * @rmmio: configuration area address on SRAM. - * @cdev: related char device. - * @cdev_ctrl: char device for control operations only (INFO IOCTL) - * @dev: related kernel basic device structure. - * @dev_ctrl: related kernel device structure for the control device - * @work_freq: delayed work to lower device frequency if possible. - * @work_heartbeat: delayed work for ArmCP is-alive check. - * @asic_name: ASIC specific nmae. - * @asic_type: ASIC specific type. - * @completion_queue: array of hl_cq. - * @cq_wq: work queues of completion queues for executing work in process - * context. - * @eq_wq: work queue of event queue for executing work in process context. - * @kernel_ctx: Kernel driver context structure. - * @kernel_queues: array of hl_hw_queue. - * @hw_queues_mirror_list: CS mirror list for TDR. - * @hw_queues_mirror_lock: protects hw_queues_mirror_list. - * @kernel_cb_mgr: command buffer manager for creating/destroying/handling CGs. - * @event_queue: event queue for IRQ from ArmCP. - * @dma_pool: DMA pool for small allocations. - * @cpu_accessible_dma_mem: Host <-> ArmCP shared memory CPU address. - * @cpu_accessible_dma_address: Host <-> ArmCP shared memory DMA address. - * @cpu_accessible_dma_pool: Host <-> ArmCP shared memory pool. - * @asid_bitmap: holds used/available ASIDs. - * @asid_mutex: protects asid_bitmap. - * @send_cpu_message_lock: enforces only one message in Host <-> ArmCP queue. - * @debug_lock: protects critical section of setting debug mode for device - * @asic_prop: ASIC specific immutable properties. - * @asic_funcs: ASIC specific functions. - * @asic_specific: ASIC specific information to use only from ASIC files. - * @mmu_pgt_pool: pool of available MMU hops. - * @vm: virtual memory manager for MMU. - * @mmu_cache_lock: protects MMU cache invalidation as it can serve one context. - * @mmu_shadow_hop0: shadow mapping of the MMU hop 0 zone. - * @hwmon_dev: H/W monitor device. - * @pm_mng_profile: current power management profile. - * @hl_chip_info: ASIC's sensors information. - * @hl_debugfs: device's debugfs manager. - * @cb_pool: list of preallocated CBs. - * @cb_pool_lock: protects the CB pool. - * @fpriv_list: list of file private data structures. Each structure is created - * when a user opens the device - * @fpriv_list_lock: protects the fpriv_list - * @compute_ctx: current compute context executing. - * @idle_busy_ts_arr: array to hold time stamps of transitions from idle to busy - * and vice-versa - * @aggregated_cs_counters: aggregated cs counters among all contexts - * @dram_used_mem: current DRAM memory consumption. - * @timeout_jiffies: device CS timeout value. - * @max_power: the max power of the device, as configured by the sysadmin. This - * value is saved so in case of hard-reset, the driver will restore - * this value and update the F/W after the re-initialization - * @in_reset: is device in reset flow. - * @curr_pll_profile: current PLL profile. - * @cs_active_cnt: number of active command submissions on this device (active - * means already in H/W queues) - * @major: habanalabs kernel driver major. - * @high_pll: high PLL profile frequency. - * @soft_reset_cnt: number of soft reset since the driver was loaded. - * @hard_reset_cnt: number of hard reset since the driver was loaded. - * @idle_busy_ts_idx: index of current entry in idle_busy_ts_arr - * @id: device minor. - * @id_control: minor of the control device - * @cpu_pci_msb_addr: 50-bit extension bits for the device CPU's 40-bit - * addresses. - * @disabled: is device disabled. - * @late_init_done: is late init stage was done during initialization. - * @hwmon_initialized: is H/W monitor sensors was initialized. - * @hard_reset_pending: is there a hard reset work pending. - * @heartbeat: is heartbeat sanity check towards ArmCP enabled. - * @reset_on_lockup: true if a reset should be done in case of stuck CS, false - * otherwise. - * @dram_supports_virtual_memory: is MMU enabled towards DRAM. - * @dram_default_page_mapping: is DRAM default page mapping enabled. - * @pmmu_huge_range: is a different virtual addresses range used for PMMU with - * huge pages. - * @init_done: is the initialization of the device done. - * @mmu_enable: is MMU enabled. - * @mmu_huge_page_opt: is MMU huge pages optimization enabled. - * @clock_gating: is clock gating enabled. - * @device_cpu_disabled: is the device CPU disabled (due to timeouts) - * @dma_mask: the dma mask that was set for this device - * @in_debug: is device under debug. This, together with fpriv_list, enforces - * that only a single user is configuring the debug infrastructure. - * @power9_64bit_dma_enable: true to enable 64-bit DMA mask support. Relevant - * only to POWER9 machines. - * @cdev_sysfs_created: were char devices and sysfs nodes created. - * @stop_on_err: true if engines should stop on error. - * @supports_sync_stream: is sync stream supported. - * @sync_stream_queue_idx: helper index for sync stream queues initialization. - * @supports_coresight: is CoreSight supported. - * @supports_soft_reset: is soft reset supported. - */ -struct hl_device { - struct pci_dev *pdev; - u64 pcie_bar_phys[HL_PCI_NUM_BARS]; - void __iomem *pcie_bar[HL_PCI_NUM_BARS]; - void __iomem *rmmio; - struct cdev cdev; - struct cdev cdev_ctrl; - struct device *dev; - struct device *dev_ctrl; - struct delayed_work work_freq; - struct delayed_work work_heartbeat; - char asic_name[16]; - enum hl_asic_type asic_type; - struct hl_cq *completion_queue; - struct workqueue_struct **cq_wq; - struct workqueue_struct *eq_wq; - struct hl_ctx *kernel_ctx; - struct hl_hw_queue *kernel_queues; - struct list_head hw_queues_mirror_list; - spinlock_t hw_queues_mirror_lock; - struct hl_cb_mgr kernel_cb_mgr; - struct hl_eq event_queue; - struct dma_pool *dma_pool; - void *cpu_accessible_dma_mem; - dma_addr_t cpu_accessible_dma_address; - struct gen_pool *cpu_accessible_dma_pool; - unsigned long *asid_bitmap; - struct mutex asid_mutex; - struct mutex send_cpu_message_lock; - struct mutex debug_lock; - struct asic_fixed_properties asic_prop; - const struct hl_asic_funcs *asic_funcs; - void *asic_specific; - struct gen_pool *mmu_pgt_pool; - struct hl_vm vm; - struct mutex mmu_cache_lock; - void *mmu_shadow_hop0; - struct device *hwmon_dev; - enum hl_pm_mng_profile pm_mng_profile; - struct hwmon_chip_info *hl_chip_info; - - struct hl_dbg_device_entry hl_debugfs; - - struct list_head cb_pool; - spinlock_t cb_pool_lock; - - struct list_head fpriv_list; - struct mutex fpriv_list_lock; - - struct hl_ctx *compute_ctx; - - struct hl_device_idle_busy_ts *idle_busy_ts_arr; - - struct hl_cs_counters aggregated_cs_counters; - - atomic64_t dram_used_mem; - u64 timeout_jiffies; - u64 max_power; - atomic_t in_reset; - enum hl_pll_frequency curr_pll_profile; - int cs_active_cnt; - u32 major; - u32 high_pll; - u32 soft_reset_cnt; - u32 hard_reset_cnt; - u32 idle_busy_ts_idx; - u16 id; - u16 id_control; - u16 cpu_pci_msb_addr; - u8 disabled; - u8 late_init_done; - u8 hwmon_initialized; - u8 hard_reset_pending; - u8 heartbeat; - u8 reset_on_lockup; - u8 dram_supports_virtual_memory; - u8 dram_default_page_mapping; - u8 pmmu_huge_range; - u8 init_done; - u8 clock_gating; - u8 device_cpu_disabled; - u8 dma_mask; - u8 in_debug; - u8 power9_64bit_dma_enable; - u8 cdev_sysfs_created; - u8 stop_on_err; - u8 supports_sync_stream; - u8 sync_stream_queue_idx; - u8 supports_coresight; - u8 supports_soft_reset; - - /* Parameters for bring-up */ - u8 mmu_enable; - u8 mmu_huge_page_opt; - u8 cpu_enable; - u8 reset_pcilink; - u8 cpu_queues_enable; - u8 fw_loading; - u8 pldm; - u8 axi_drain; - u8 sram_scrambler_enable; - u8 dram_scrambler_enable; - u8 hard_reset_on_fw_events; - u8 bmc_enable; - u8 rl_enable; -}; - - -/* - * IOCTLs - */ - -/** - * typedef hl_ioctl_t - typedef for ioctl function in the driver - * @hpriv: pointer to the FD's private data, which contains state of - * user process - * @data: pointer to the input/output arguments structure of the IOCTL - * - * Return: 0 for success, negative value for error - */ -typedef int hl_ioctl_t(struct hl_fpriv *hpriv, void *data); - -/** - * struct hl_ioctl_desc - describes an IOCTL entry of the driver. - * @cmd: the IOCTL code as created by the kernel macros. - * @func: pointer to the driver's function that should be called for this IOCTL. - */ -struct hl_ioctl_desc { - unsigned int cmd; - hl_ioctl_t *func; -}; - - -/* - * Kernel module functions that can be accessed by entire module - */ - -/** - * hl_mem_area_inside_range() - Checks whether address+size are inside a range. - * @address: The start address of the area we want to validate. - * @size: The size in bytes of the area we want to validate. - * @range_start_address: The start address of the valid range. - * @range_end_address: The end address of the valid range. - * - * Return: true if the area is inside the valid range, false otherwise. - */ -static inline bool hl_mem_area_inside_range(u64 address, u32 size, - u64 range_start_address, u64 range_end_address) -{ - u64 end_address = address + size; - - if ((address >= range_start_address) && - (end_address <= range_end_address) && - (end_address > address)) - return true; - - return false; -} - -/** - * hl_mem_area_crosses_range() - Checks whether address+size crossing a range. - * @address: The start address of the area we want to validate. - * @size: The size in bytes of the area we want to validate. - * @range_start_address: The start address of the valid range. - * @range_end_address: The end address of the valid range. - * - * Return: true if the area overlaps part or all of the valid range, - * false otherwise. - */ -static inline bool hl_mem_area_crosses_range(u64 address, u32 size, - u64 range_start_address, u64 range_end_address) -{ - u64 end_address = address + size; - - if ((address >= range_start_address) && - (address < range_end_address)) - return true; - - if ((end_address >= range_start_address) && - (end_address < range_end_address)) - return true; - - if ((address < range_start_address) && - (end_address >= range_end_address)) - return true; - - return false; -} - -int hl_device_open(struct inode *inode, struct file *filp); -int hl_device_open_ctrl(struct inode *inode, struct file *filp); -bool hl_device_disabled_or_in_reset(struct hl_device *hdev); -enum hl_device_status hl_device_status(struct hl_device *hdev); -int hl_device_set_debug_mode(struct hl_device *hdev, bool enable); -int create_hdev(struct hl_device **dev, struct pci_dev *pdev, - enum hl_asic_type asic_type, int minor); -void destroy_hdev(struct hl_device *hdev); -int hl_hw_queues_create(struct hl_device *hdev); -void hl_hw_queues_destroy(struct hl_device *hdev); -int hl_hw_queue_send_cb_no_cmpl(struct hl_device *hdev, u32 hw_queue_id, - u32 cb_size, u64 cb_ptr); -int hl_hw_queue_schedule_cs(struct hl_cs *cs); -u32 hl_hw_queue_add_ptr(u32 ptr, u16 val); -void hl_hw_queue_inc_ci_kernel(struct hl_device *hdev, u32 hw_queue_id); -void hl_int_hw_queue_update_ci(struct hl_cs *cs); -void hl_hw_queue_reset(struct hl_device *hdev, bool hard_reset); - -#define hl_queue_inc_ptr(p) hl_hw_queue_add_ptr(p, 1) -#define hl_pi_2_offset(pi) ((pi) & (HL_QUEUE_LENGTH - 1)) - -int hl_cq_init(struct hl_device *hdev, struct hl_cq *q, u32 hw_queue_id); -void hl_cq_fini(struct hl_device *hdev, struct hl_cq *q); -int hl_eq_init(struct hl_device *hdev, struct hl_eq *q); -void hl_eq_fini(struct hl_device *hdev, struct hl_eq *q); -void hl_cq_reset(struct hl_device *hdev, struct hl_cq *q); -void hl_eq_reset(struct hl_device *hdev, struct hl_eq *q); -irqreturn_t hl_irq_handler_cq(int irq, void *arg); -irqreturn_t hl_irq_handler_eq(int irq, void *arg); -u32 hl_cq_inc_ptr(u32 ptr); - -int hl_asid_init(struct hl_device *hdev); -void hl_asid_fini(struct hl_device *hdev); -unsigned long hl_asid_alloc(struct hl_device *hdev); -void hl_asid_free(struct hl_device *hdev, unsigned long asid); - -int hl_ctx_create(struct hl_device *hdev, struct hl_fpriv *hpriv); -void hl_ctx_free(struct hl_device *hdev, struct hl_ctx *ctx); -int hl_ctx_init(struct hl_device *hdev, struct hl_ctx *ctx, bool is_kernel_ctx); -void hl_ctx_do_release(struct kref *ref); -void hl_ctx_get(struct hl_device *hdev, struct hl_ctx *ctx); -int hl_ctx_put(struct hl_ctx *ctx); -struct dma_fence *hl_ctx_get_fence(struct hl_ctx *ctx, u64 seq); -void hl_ctx_mgr_init(struct hl_ctx_mgr *mgr); -void hl_ctx_mgr_fini(struct hl_device *hdev, struct hl_ctx_mgr *mgr); - -int hl_device_init(struct hl_device *hdev, struct class *hclass); -void hl_device_fini(struct hl_device *hdev); -int hl_device_suspend(struct hl_device *hdev); -int hl_device_resume(struct hl_device *hdev); -int hl_device_reset(struct hl_device *hdev, bool hard_reset, - bool from_hard_reset_thread); -void hl_hpriv_get(struct hl_fpriv *hpriv); -void hl_hpriv_put(struct hl_fpriv *hpriv); -int hl_device_set_frequency(struct hl_device *hdev, enum hl_pll_frequency freq); -uint32_t hl_device_utilization(struct hl_device *hdev, uint32_t period_ms); - -int hl_build_hwmon_channel_info(struct hl_device *hdev, - struct armcp_sensor *sensors_arr); - -int hl_sysfs_init(struct hl_device *hdev); -void hl_sysfs_fini(struct hl_device *hdev); - -int hl_hwmon_init(struct hl_device *hdev); -void hl_hwmon_fini(struct hl_device *hdev); - -int hl_cb_create(struct hl_device *hdev, struct hl_cb_mgr *mgr, u32 cb_size, - u64 *handle, int ctx_id); -int hl_cb_destroy(struct hl_device *hdev, struct hl_cb_mgr *mgr, u64 cb_handle); -int hl_cb_mmap(struct hl_fpriv *hpriv, struct vm_area_struct *vma); -struct hl_cb *hl_cb_get(struct hl_device *hdev, struct hl_cb_mgr *mgr, - u32 handle); -void hl_cb_put(struct hl_cb *cb); -void hl_cb_mgr_init(struct hl_cb_mgr *mgr); -void hl_cb_mgr_fini(struct hl_device *hdev, struct hl_cb_mgr *mgr); -struct hl_cb *hl_cb_kernel_create(struct hl_device *hdev, u32 cb_size); -int hl_cb_pool_init(struct hl_device *hdev); -int hl_cb_pool_fini(struct hl_device *hdev); - -void hl_cs_rollback_all(struct hl_device *hdev); -struct hl_cs_job *hl_cs_allocate_job(struct hl_device *hdev, - enum hl_queue_type queue_type, bool is_kernel_allocated_cb); -void hl_sob_reset_error(struct kref *ref); - -void goya_set_asic_funcs(struct hl_device *hdev); -void gaudi_set_asic_funcs(struct hl_device *hdev); - -int hl_vm_ctx_init(struct hl_ctx *ctx); -void hl_vm_ctx_fini(struct hl_ctx *ctx); - -int hl_vm_init(struct hl_device *hdev); -void hl_vm_fini(struct hl_device *hdev); - -int hl_pin_host_memory(struct hl_device *hdev, u64 addr, u64 size, - struct hl_userptr *userptr); -void hl_unpin_host_memory(struct hl_device *hdev, struct hl_userptr *userptr); -void hl_userptr_delete_list(struct hl_device *hdev, - struct list_head *userptr_list); -bool hl_userptr_is_pinned(struct hl_device *hdev, u64 addr, u32 size, - struct list_head *userptr_list, - struct hl_userptr **userptr); - -int hl_mmu_init(struct hl_device *hdev); -void hl_mmu_fini(struct hl_device *hdev); -int hl_mmu_ctx_init(struct hl_ctx *ctx); -void hl_mmu_ctx_fini(struct hl_ctx *ctx); -int hl_mmu_map(struct hl_ctx *ctx, u64 virt_addr, u64 phys_addr, - u32 page_size, bool flush_pte); -int hl_mmu_unmap(struct hl_ctx *ctx, u64 virt_addr, u32 page_size, - bool flush_pte); -void hl_mmu_swap_out(struct hl_ctx *ctx); -void hl_mmu_swap_in(struct hl_ctx *ctx); - -int hl_fw_load_fw_to_device(struct hl_device *hdev, const char *fw_name, - void __iomem *dst); -int hl_fw_send_pci_access_msg(struct hl_device *hdev, u32 opcode); -int hl_fw_send_cpu_message(struct hl_device *hdev, u32 hw_queue_id, u32 *msg, - u16 len, u32 timeout, long *result); -int hl_fw_unmask_irq(struct hl_device *hdev, u16 event_type); -int hl_fw_unmask_irq_arr(struct hl_device *hdev, const u32 *irq_arr, - size_t irq_arr_size); -int hl_fw_test_cpu_queue(struct hl_device *hdev); -void *hl_fw_cpu_accessible_dma_pool_alloc(struct hl_device *hdev, size_t size, - dma_addr_t *dma_handle); -void hl_fw_cpu_accessible_dma_pool_free(struct hl_device *hdev, size_t size, - void *vaddr); -int hl_fw_send_heartbeat(struct hl_device *hdev); -int hl_fw_armcp_info_get(struct hl_device *hdev); -int hl_fw_get_eeprom_data(struct hl_device *hdev, void *data, size_t max_size); -int hl_fw_init_cpu(struct hl_device *hdev, u32 cpu_boot_status_reg, - u32 msg_to_cpu_reg, u32 cpu_msg_status_reg, - u32 boot_err0_reg, bool skip_bmc, - u32 cpu_timeout, u32 boot_fit_timeout); - -int hl_pci_bars_map(struct hl_device *hdev, const char * const name[3], - bool is_wc[3]); -int hl_pci_iatu_write(struct hl_device *hdev, u32 addr, u32 data); -int hl_pci_set_dram_bar_base(struct hl_device *hdev, u8 inbound_region, u8 bar, - u64 addr); -int hl_pci_set_inbound_region(struct hl_device *hdev, u8 region, - struct hl_inbound_pci_region *pci_region); -int hl_pci_set_outbound_region(struct hl_device *hdev, - struct hl_outbound_pci_region *pci_region); -int hl_pci_init(struct hl_device *hdev); -void hl_pci_fini(struct hl_device *hdev); - -long hl_get_frequency(struct hl_device *hdev, u32 pll_index, bool curr); -void hl_set_frequency(struct hl_device *hdev, u32 pll_index, u64 freq); -int hl_get_temperature(struct hl_device *hdev, - int sensor_index, u32 attr, long *value); -int hl_set_temperature(struct hl_device *hdev, - int sensor_index, u32 attr, long value); -int hl_get_voltage(struct hl_device *hdev, - int sensor_index, u32 attr, long *value); -int hl_get_current(struct hl_device *hdev, - int sensor_index, u32 attr, long *value); -int hl_get_fan_speed(struct hl_device *hdev, - int sensor_index, u32 attr, long *value); -int hl_get_pwm_info(struct hl_device *hdev, - int sensor_index, u32 attr, long *value); -void hl_set_pwm_info(struct hl_device *hdev, int sensor_index, u32 attr, - long value); -u64 hl_get_max_power(struct hl_device *hdev); -void hl_set_max_power(struct hl_device *hdev, u64 value); -int hl_set_voltage(struct hl_device *hdev, - int sensor_index, u32 attr, long value); -int hl_set_current(struct hl_device *hdev, - int sensor_index, u32 attr, long value); - -#ifdef CONFIG_DEBUG_FS - -void hl_debugfs_init(void); -void hl_debugfs_fini(void); -void hl_debugfs_add_device(struct hl_device *hdev); -void hl_debugfs_remove_device(struct hl_device *hdev); -void hl_debugfs_add_file(struct hl_fpriv *hpriv); -void hl_debugfs_remove_file(struct hl_fpriv *hpriv); -void hl_debugfs_add_cb(struct hl_cb *cb); -void hl_debugfs_remove_cb(struct hl_cb *cb); -void hl_debugfs_add_cs(struct hl_cs *cs); -void hl_debugfs_remove_cs(struct hl_cs *cs); -void hl_debugfs_add_job(struct hl_device *hdev, struct hl_cs_job *job); -void hl_debugfs_remove_job(struct hl_device *hdev, struct hl_cs_job *job); -void hl_debugfs_add_userptr(struct hl_device *hdev, struct hl_userptr *userptr); -void hl_debugfs_remove_userptr(struct hl_device *hdev, - struct hl_userptr *userptr); -void hl_debugfs_add_ctx_mem_hash(struct hl_device *hdev, struct hl_ctx *ctx); -void hl_debugfs_remove_ctx_mem_hash(struct hl_device *hdev, struct hl_ctx *ctx); - -#else - -static inline void __init hl_debugfs_init(void) -{ -} - -static inline void hl_debugfs_fini(void) -{ -} - -static inline void hl_debugfs_add_device(struct hl_device *hdev) -{ -} - -static inline void hl_debugfs_remove_device(struct hl_device *hdev) -{ -} - -static inline void hl_debugfs_add_file(struct hl_fpriv *hpriv) -{ -} - -static inline void hl_debugfs_remove_file(struct hl_fpriv *hpriv) -{ -} - -static inline void hl_debugfs_add_cb(struct hl_cb *cb) -{ -} - -static inline void hl_debugfs_remove_cb(struct hl_cb *cb) -{ -} - -static inline void hl_debugfs_add_cs(struct hl_cs *cs) -{ -} - -static inline void hl_debugfs_remove_cs(struct hl_cs *cs) -{ -} - -static inline void hl_debugfs_add_job(struct hl_device *hdev, - struct hl_cs_job *job) -{ -} - -static inline void hl_debugfs_remove_job(struct hl_device *hdev, - struct hl_cs_job *job) -{ -} - -static inline void hl_debugfs_add_userptr(struct hl_device *hdev, - struct hl_userptr *userptr) -{ -} - -static inline void hl_debugfs_remove_userptr(struct hl_device *hdev, - struct hl_userptr *userptr) -{ -} - -static inline void hl_debugfs_add_ctx_mem_hash(struct hl_device *hdev, - struct hl_ctx *ctx) -{ -} - -static inline void hl_debugfs_remove_ctx_mem_hash(struct hl_device *hdev, - struct hl_ctx *ctx) -{ -} - -#endif - -/* IOCTLs */ -long hl_ioctl(struct file *filep, unsigned int cmd, unsigned long arg); -long hl_ioctl_control(struct file *filep, unsigned int cmd, unsigned long arg); -int hl_cb_ioctl(struct hl_fpriv *hpriv, void *data); -int hl_cs_ioctl(struct hl_fpriv *hpriv, void *data); -int hl_cs_wait_ioctl(struct hl_fpriv *hpriv, void *data); -int hl_mem_ioctl(struct hl_fpriv *hpriv, void *data); - -#endif /* HABANALABSP_H_ */ diff --git a/drivers/misc/habanalabs/habanalabs_drv.c b/drivers/misc/habanalabs/habanalabs_drv.c deleted file mode 100644 index f38664b03865..000000000000 --- a/drivers/misc/habanalabs/habanalabs_drv.c +++ /dev/null @@ -1,529 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -/* - * Copyright 2016-2019 HabanaLabs, Ltd. - * All Rights Reserved. - * - */ - -#define pr_fmt(fmt) "habanalabs: " fmt - -#include "habanalabs.h" - -#include -#include - -#define HL_DRIVER_AUTHOR "HabanaLabs Kernel Driver Team" - -#define HL_DRIVER_DESC "Driver for HabanaLabs's AI Accelerators" - -MODULE_AUTHOR(HL_DRIVER_AUTHOR); -MODULE_DESCRIPTION(HL_DRIVER_DESC); -MODULE_LICENSE("GPL v2"); - -static int hl_major; -static struct class *hl_class; -static DEFINE_IDR(hl_devs_idr); -static DEFINE_MUTEX(hl_devs_idr_lock); - -static int timeout_locked = 5; -static int reset_on_lockup = 1; - -module_param(timeout_locked, int, 0444); -MODULE_PARM_DESC(timeout_locked, - "Device lockup timeout in seconds (0 = disabled, default 5s)"); - -module_param(reset_on_lockup, int, 0444); -MODULE_PARM_DESC(reset_on_lockup, - "Do device reset on lockup (0 = no, 1 = yes, default yes)"); - -#define PCI_VENDOR_ID_HABANALABS 0x1da3 - -#define PCI_IDS_GOYA 0x0001 -#define PCI_IDS_GAUDI 0x1000 - -static const struct pci_device_id ids[] = { - { PCI_DEVICE(PCI_VENDOR_ID_HABANALABS, PCI_IDS_GOYA), }, - { PCI_DEVICE(PCI_VENDOR_ID_HABANALABS, PCI_IDS_GAUDI), }, - { 0, } -}; -MODULE_DEVICE_TABLE(pci, ids); - -/* - * get_asic_type - translate device id to asic type - * - * @device: id of the PCI device - * - * Translate device id to asic type. - * In case of unidentified device, return -1 - */ -static enum hl_asic_type get_asic_type(u16 device) -{ - enum hl_asic_type asic_type; - - switch (device) { - case PCI_IDS_GOYA: - asic_type = ASIC_GOYA; - break; - case PCI_IDS_GAUDI: - asic_type = ASIC_GAUDI; - break; - default: - asic_type = ASIC_INVALID; - break; - } - - return asic_type; -} - -/* - * hl_device_open - open function for habanalabs device - * - * @inode: pointer to inode structure - * @filp: pointer to file structure - * - * Called when process opens an habanalabs device. - */ -int hl_device_open(struct inode *inode, struct file *filp) -{ - struct hl_device *hdev; - struct hl_fpriv *hpriv; - int rc; - - mutex_lock(&hl_devs_idr_lock); - hdev = idr_find(&hl_devs_idr, iminor(inode)); - mutex_unlock(&hl_devs_idr_lock); - - if (!hdev) { - pr_err("Couldn't find device %d:%d\n", - imajor(inode), iminor(inode)); - return -ENXIO; - } - - hpriv = kzalloc(sizeof(*hpriv), GFP_KERNEL); - if (!hpriv) - return -ENOMEM; - - hpriv->hdev = hdev; - filp->private_data = hpriv; - hpriv->filp = filp; - mutex_init(&hpriv->restore_phase_mutex); - kref_init(&hpriv->refcount); - nonseekable_open(inode, filp); - - hl_cb_mgr_init(&hpriv->cb_mgr); - hl_ctx_mgr_init(&hpriv->ctx_mgr); - - hpriv->taskpid = find_get_pid(current->pid); - - mutex_lock(&hdev->fpriv_list_lock); - - if (hl_device_disabled_or_in_reset(hdev)) { - dev_err_ratelimited(hdev->dev, - "Can't open %s because it is disabled or in reset\n", - dev_name(hdev->dev)); - rc = -EPERM; - goto out_err; - } - - if (hdev->in_debug) { - dev_err_ratelimited(hdev->dev, - "Can't open %s because it is being debugged by another user\n", - dev_name(hdev->dev)); - rc = -EPERM; - goto out_err; - } - - if (hdev->compute_ctx) { - dev_dbg_ratelimited(hdev->dev, - "Can't open %s because another user is working on it\n", - dev_name(hdev->dev)); - rc = -EBUSY; - goto out_err; - } - - rc = hl_ctx_create(hdev, hpriv); - if (rc) { - dev_err(hdev->dev, "Failed to create context %d\n", rc); - goto out_err; - } - - /* Device is IDLE at this point so it is legal to change PLLs. - * There is no need to check anything because if the PLL is - * already HIGH, the set function will return without doing - * anything - */ - hl_device_set_frequency(hdev, PLL_HIGH); - - list_add(&hpriv->dev_node, &hdev->fpriv_list); - mutex_unlock(&hdev->fpriv_list_lock); - - hl_debugfs_add_file(hpriv); - - return 0; - -out_err: - mutex_unlock(&hdev->fpriv_list_lock); - - hl_cb_mgr_fini(hpriv->hdev, &hpriv->cb_mgr); - hl_ctx_mgr_fini(hpriv->hdev, &hpriv->ctx_mgr); - filp->private_data = NULL; - mutex_destroy(&hpriv->restore_phase_mutex); - put_pid(hpriv->taskpid); - - kfree(hpriv); - - return rc; -} - -int hl_device_open_ctrl(struct inode *inode, struct file *filp) -{ - struct hl_device *hdev; - struct hl_fpriv *hpriv; - int rc; - - mutex_lock(&hl_devs_idr_lock); - hdev = idr_find(&hl_devs_idr, iminor(inode)); - mutex_unlock(&hl_devs_idr_lock); - - if (!hdev) { - pr_err("Couldn't find device %d:%d\n", - imajor(inode), iminor(inode)); - return -ENXIO; - } - - hpriv = kzalloc(sizeof(*hpriv), GFP_KERNEL); - if (!hpriv) - return -ENOMEM; - - mutex_lock(&hdev->fpriv_list_lock); - - if (hl_device_disabled_or_in_reset(hdev)) { - dev_err_ratelimited(hdev->dev_ctrl, - "Can't open %s because it is disabled or in reset\n", - dev_name(hdev->dev_ctrl)); - rc = -EPERM; - goto out_err; - } - - list_add(&hpriv->dev_node, &hdev->fpriv_list); - mutex_unlock(&hdev->fpriv_list_lock); - - hpriv->hdev = hdev; - filp->private_data = hpriv; - hpriv->filp = filp; - hpriv->is_control = true; - nonseekable_open(inode, filp); - - hpriv->taskpid = find_get_pid(current->pid); - - return 0; - -out_err: - mutex_unlock(&hdev->fpriv_list_lock); - kfree(hpriv); - return rc; -} - -static void set_driver_behavior_per_device(struct hl_device *hdev) -{ - hdev->mmu_enable = 1; - hdev->cpu_enable = 1; - hdev->fw_loading = 1; - hdev->cpu_queues_enable = 1; - hdev->heartbeat = 1; - hdev->clock_gating = 1; - - hdev->reset_pcilink = 0; - hdev->axi_drain = 0; - hdev->sram_scrambler_enable = 1; - hdev->dram_scrambler_enable = 1; - hdev->bmc_enable = 1; - hdev->hard_reset_on_fw_events = 1; -} - -/* - * create_hdev - create habanalabs device instance - * - * @dev: will hold the pointer to the new habanalabs device structure - * @pdev: pointer to the pci device - * @asic_type: in case of simulator device, which device is it - * @minor: in case of simulator device, the minor of the device - * - * Allocate memory for habanalabs device and initialize basic fields - * Identify the ASIC type - * Allocate ID (minor) for the device (only for real devices) - */ -int create_hdev(struct hl_device **dev, struct pci_dev *pdev, - enum hl_asic_type asic_type, int minor) -{ - struct hl_device *hdev; - int rc, main_id, ctrl_id = 0; - - *dev = NULL; - - hdev = kzalloc(sizeof(*hdev), GFP_KERNEL); - if (!hdev) - return -ENOMEM; - - /* First, we must find out which ASIC are we handling. This is needed - * to configure the behavior of the driver (kernel parameters) - */ - if (pdev) { - hdev->asic_type = get_asic_type(pdev->device); - if (hdev->asic_type == ASIC_INVALID) { - dev_err(&pdev->dev, "Unsupported ASIC\n"); - rc = -ENODEV; - goto free_hdev; - } - } else { - hdev->asic_type = asic_type; - } - - hdev->major = hl_major; - hdev->reset_on_lockup = reset_on_lockup; - hdev->pldm = 0; - - set_driver_behavior_per_device(hdev); - - if (timeout_locked) - hdev->timeout_jiffies = msecs_to_jiffies(timeout_locked * 1000); - else - hdev->timeout_jiffies = MAX_SCHEDULE_TIMEOUT; - - hdev->disabled = true; - hdev->pdev = pdev; /* can be NULL in case of simulator device */ - - /* Set default DMA mask to 32 bits */ - hdev->dma_mask = 32; - - mutex_lock(&hl_devs_idr_lock); - - /* Always save 2 numbers, 1 for main device and 1 for control. - * They must be consecutive - */ - main_id = idr_alloc(&hl_devs_idr, hdev, 0, HL_MAX_MINORS, - GFP_KERNEL); - - if (main_id >= 0) - ctrl_id = idr_alloc(&hl_devs_idr, hdev, main_id + 1, - main_id + 2, GFP_KERNEL); - - mutex_unlock(&hl_devs_idr_lock); - - if ((main_id < 0) || (ctrl_id < 0)) { - if ((main_id == -ENOSPC) || (ctrl_id == -ENOSPC)) - pr_err("too many devices in the system\n"); - - if (main_id >= 0) { - mutex_lock(&hl_devs_idr_lock); - idr_remove(&hl_devs_idr, main_id); - mutex_unlock(&hl_devs_idr_lock); - } - - rc = -EBUSY; - goto free_hdev; - } - - hdev->id = main_id; - hdev->id_control = ctrl_id; - - *dev = hdev; - - return 0; - -free_hdev: - kfree(hdev); - return rc; -} - -/* - * destroy_hdev - destroy habanalabs device instance - * - * @dev: pointer to the habanalabs device structure - * - */ -void destroy_hdev(struct hl_device *hdev) -{ - /* Remove device from the device list */ - mutex_lock(&hl_devs_idr_lock); - idr_remove(&hl_devs_idr, hdev->id); - idr_remove(&hl_devs_idr, hdev->id_control); - mutex_unlock(&hl_devs_idr_lock); - - kfree(hdev); -} - -static int hl_pmops_suspend(struct device *dev) -{ - struct hl_device *hdev = dev_get_drvdata(dev); - - pr_debug("Going to suspend PCI device\n"); - - if (!hdev) { - pr_err("device pointer is NULL in suspend\n"); - return 0; - } - - return hl_device_suspend(hdev); -} - -static int hl_pmops_resume(struct device *dev) -{ - struct hl_device *hdev = dev_get_drvdata(dev); - - pr_debug("Going to resume PCI device\n"); - - if (!hdev) { - pr_err("device pointer is NULL in resume\n"); - return 0; - } - - return hl_device_resume(hdev); -} - -/* - * hl_pci_probe - probe PCI habanalabs devices - * - * @pdev: pointer to pci device - * @id: pointer to pci device id structure - * - * Standard PCI probe function for habanalabs device. - * Create a new habanalabs device and initialize it according to the - * device's type - */ -static int hl_pci_probe(struct pci_dev *pdev, - const struct pci_device_id *id) -{ - struct hl_device *hdev; - int rc; - - dev_info(&pdev->dev, HL_NAME - " device found [%04x:%04x] (rev %x)\n", - (int)pdev->vendor, (int)pdev->device, (int)pdev->revision); - - rc = create_hdev(&hdev, pdev, ASIC_INVALID, -1); - if (rc) - return rc; - - pci_set_drvdata(pdev, hdev); - - rc = hl_device_init(hdev, hl_class); - if (rc) { - dev_err(&pdev->dev, "Fatal error during habanalabs device init\n"); - rc = -ENODEV; - goto disable_device; - } - - return 0; - -disable_device: - pci_set_drvdata(pdev, NULL); - destroy_hdev(hdev); - - return rc; -} - -/* - * hl_pci_remove - remove PCI habanalabs devices - * - * @pdev: pointer to pci device - * - * Standard PCI remove function for habanalabs device - */ -static void hl_pci_remove(struct pci_dev *pdev) -{ - struct hl_device *hdev; - - hdev = pci_get_drvdata(pdev); - if (!hdev) - return; - - hl_device_fini(hdev); - pci_set_drvdata(pdev, NULL); - - destroy_hdev(hdev); -} - -static const struct dev_pm_ops hl_pm_ops = { - .suspend = hl_pmops_suspend, - .resume = hl_pmops_resume, -}; - -static struct pci_driver hl_pci_driver = { - .name = HL_NAME, - .id_table = ids, - .probe = hl_pci_probe, - .remove = hl_pci_remove, - .driver.pm = &hl_pm_ops, -}; - -/* - * hl_init - Initialize the habanalabs kernel driver - */ -static int __init hl_init(void) -{ - int rc; - dev_t dev; - - pr_info("loading driver\n"); - - rc = alloc_chrdev_region(&dev, 0, HL_MAX_MINORS, HL_NAME); - if (rc < 0) { - pr_err("unable to get major\n"); - return rc; - } - - hl_major = MAJOR(dev); - - hl_class = class_create(THIS_MODULE, HL_NAME); - if (IS_ERR(hl_class)) { - pr_err("failed to allocate class\n"); - rc = PTR_ERR(hl_class); - goto remove_major; - } - - hl_debugfs_init(); - - rc = pci_register_driver(&hl_pci_driver); - if (rc) { - pr_err("failed to register pci device\n"); - goto remove_debugfs; - } - - pr_debug("driver loaded\n"); - - return 0; - -remove_debugfs: - hl_debugfs_fini(); - class_destroy(hl_class); -remove_major: - unregister_chrdev_region(MKDEV(hl_major, 0), HL_MAX_MINORS); - return rc; -} - -/* - * hl_exit - Release all resources of the habanalabs kernel driver - */ -static void __exit hl_exit(void) -{ - pci_unregister_driver(&hl_pci_driver); - - /* - * Removing debugfs must be after all devices or simulator devices - * have been removed because otherwise we get a bug in the - * debugfs module for referencing NULL objects - */ - hl_debugfs_fini(); - - class_destroy(hl_class); - unregister_chrdev_region(MKDEV(hl_major, 0), HL_MAX_MINORS); - - idr_destroy(&hl_devs_idr); - - pr_debug("driver removed\n"); -} - -module_init(hl_init); -module_exit(hl_exit); diff --git a/drivers/misc/habanalabs/habanalabs_ioctl.c b/drivers/misc/habanalabs/habanalabs_ioctl.c deleted file mode 100644 index 5af1c03da473..000000000000 --- a/drivers/misc/habanalabs/habanalabs_ioctl.c +++ /dev/null @@ -1,546 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -/* - * Copyright 2016-2019 HabanaLabs, Ltd. - * All Rights Reserved. - */ - -#include -#include "habanalabs.h" - -#include -#include -#include - -static u32 hl_debug_struct_size[HL_DEBUG_OP_TIMESTAMP + 1] = { - [HL_DEBUG_OP_ETR] = sizeof(struct hl_debug_params_etr), - [HL_DEBUG_OP_ETF] = sizeof(struct hl_debug_params_etf), - [HL_DEBUG_OP_STM] = sizeof(struct hl_debug_params_stm), - [HL_DEBUG_OP_FUNNEL] = 0, - [HL_DEBUG_OP_BMON] = sizeof(struct hl_debug_params_bmon), - [HL_DEBUG_OP_SPMU] = sizeof(struct hl_debug_params_spmu), - [HL_DEBUG_OP_TIMESTAMP] = 0 - -}; - -static int device_status_info(struct hl_device *hdev, struct hl_info_args *args) -{ - struct hl_info_device_status dev_stat = {0}; - u32 size = args->return_size; - void __user *out = (void __user *) (uintptr_t) args->return_pointer; - - if ((!size) || (!out)) - return -EINVAL; - - dev_stat.status = hl_device_status(hdev); - - return copy_to_user(out, &dev_stat, - min((size_t)size, sizeof(dev_stat))) ? -EFAULT : 0; -} - -static int hw_ip_info(struct hl_device *hdev, struct hl_info_args *args) -{ - struct hl_info_hw_ip_info hw_ip = {0}; - u32 size = args->return_size; - void __user *out = (void __user *) (uintptr_t) args->return_pointer; - struct asic_fixed_properties *prop = &hdev->asic_prop; - u64 sram_kmd_size, dram_kmd_size; - - if ((!size) || (!out)) - return -EINVAL; - - sram_kmd_size = (prop->sram_user_base_address - - prop->sram_base_address); - dram_kmd_size = (prop->dram_user_base_address - - prop->dram_base_address); - - hw_ip.device_id = hdev->asic_funcs->get_pci_id(hdev); - hw_ip.sram_base_address = prop->sram_user_base_address; - hw_ip.dram_base_address = prop->dram_user_base_address; - hw_ip.tpc_enabled_mask = prop->tpc_enabled_mask; - hw_ip.sram_size = prop->sram_size - sram_kmd_size; - hw_ip.dram_size = prop->dram_size - dram_kmd_size; - if (hw_ip.dram_size > PAGE_SIZE) - hw_ip.dram_enabled = 1; - hw_ip.num_of_events = prop->num_of_events; - - memcpy(hw_ip.armcp_version, prop->armcp_info.armcp_version, - min(VERSION_MAX_LEN, HL_INFO_VERSION_MAX_LEN)); - - memcpy(hw_ip.card_name, prop->armcp_info.card_name, - min(CARD_NAME_MAX_LEN, HL_INFO_CARD_NAME_MAX_LEN)); - - hw_ip.armcp_cpld_version = le32_to_cpu(prop->armcp_info.cpld_version); - hw_ip.module_id = le32_to_cpu(prop->armcp_info.card_location); - - hw_ip.psoc_pci_pll_nr = prop->psoc_pci_pll_nr; - hw_ip.psoc_pci_pll_nf = prop->psoc_pci_pll_nf; - hw_ip.psoc_pci_pll_od = prop->psoc_pci_pll_od; - hw_ip.psoc_pci_pll_div_factor = prop->psoc_pci_pll_div_factor; - - return copy_to_user(out, &hw_ip, - min((size_t)size, sizeof(hw_ip))) ? -EFAULT : 0; -} - -static int hw_events_info(struct hl_device *hdev, bool aggregate, - struct hl_info_args *args) -{ - u32 size, max_size = args->return_size; - void __user *out = (void __user *) (uintptr_t) args->return_pointer; - void *arr; - - if ((!max_size) || (!out)) - return -EINVAL; - - arr = hdev->asic_funcs->get_events_stat(hdev, aggregate, &size); - - return copy_to_user(out, arr, min(max_size, size)) ? -EFAULT : 0; -} - -static int dram_usage_info(struct hl_fpriv *hpriv, struct hl_info_args *args) -{ - struct hl_device *hdev = hpriv->hdev; - struct hl_info_dram_usage dram_usage = {0}; - u32 max_size = args->return_size; - void __user *out = (void __user *) (uintptr_t) args->return_pointer; - struct asic_fixed_properties *prop = &hdev->asic_prop; - u64 dram_kmd_size; - - if ((!max_size) || (!out)) - return -EINVAL; - - dram_kmd_size = (prop->dram_user_base_address - - prop->dram_base_address); - dram_usage.dram_free_mem = (prop->dram_size - dram_kmd_size) - - atomic64_read(&hdev->dram_used_mem); - if (hpriv->ctx) - dram_usage.ctx_dram_mem = - atomic64_read(&hpriv->ctx->dram_phys_mem); - - return copy_to_user(out, &dram_usage, - min((size_t) max_size, sizeof(dram_usage))) ? -EFAULT : 0; -} - -static int hw_idle(struct hl_device *hdev, struct hl_info_args *args) -{ - struct hl_info_hw_idle hw_idle = {0}; - u32 max_size = args->return_size; - void __user *out = (void __user *) (uintptr_t) args->return_pointer; - - if ((!max_size) || (!out)) - return -EINVAL; - - hw_idle.is_idle = hdev->asic_funcs->is_device_idle(hdev, - &hw_idle.busy_engines_mask, NULL); - - return copy_to_user(out, &hw_idle, - min((size_t) max_size, sizeof(hw_idle))) ? -EFAULT : 0; -} - -static int debug_coresight(struct hl_device *hdev, struct hl_debug_args *args) -{ - struct hl_debug_params *params; - void *input = NULL, *output = NULL; - int rc; - - params = kzalloc(sizeof(*params), GFP_KERNEL); - if (!params) - return -ENOMEM; - - params->reg_idx = args->reg_idx; - params->enable = args->enable; - params->op = args->op; - - if (args->input_ptr && args->input_size) { - input = kzalloc(hl_debug_struct_size[args->op], GFP_KERNEL); - if (!input) { - rc = -ENOMEM; - goto out; - } - - if (copy_from_user(input, u64_to_user_ptr(args->input_ptr), - args->input_size)) { - rc = -EFAULT; - dev_err(hdev->dev, "failed to copy input debug data\n"); - goto out; - } - - params->input = input; - } - - if (args->output_ptr && args->output_size) { - output = kzalloc(args->output_size, GFP_KERNEL); - if (!output) { - rc = -ENOMEM; - goto out; - } - - params->output = output; - params->output_size = args->output_size; - } - - rc = hdev->asic_funcs->debug_coresight(hdev, params); - if (rc) { - dev_err(hdev->dev, - "debug coresight operation failed %d\n", rc); - goto out; - } - - if (output && copy_to_user((void __user *) (uintptr_t) args->output_ptr, - output, args->output_size)) { - dev_err(hdev->dev, "copy to user failed in debug ioctl\n"); - rc = -EFAULT; - goto out; - } - - -out: - kfree(params); - kfree(output); - kfree(input); - - return rc; -} - -static int device_utilization(struct hl_device *hdev, struct hl_info_args *args) -{ - struct hl_info_device_utilization device_util = {0}; - u32 max_size = args->return_size; - void __user *out = (void __user *) (uintptr_t) args->return_pointer; - - if ((!max_size) || (!out)) - return -EINVAL; - - if ((args->period_ms < 100) || (args->period_ms > 1000) || - (args->period_ms % 100)) { - dev_err(hdev->dev, - "period %u must be between 100 - 1000 and must be divisible by 100\n", - args->period_ms); - return -EINVAL; - } - - device_util.utilization = hl_device_utilization(hdev, args->period_ms); - - return copy_to_user(out, &device_util, - min((size_t) max_size, sizeof(device_util))) ? -EFAULT : 0; -} - -static int get_clk_rate(struct hl_device *hdev, struct hl_info_args *args) -{ - struct hl_info_clk_rate clk_rate = {0}; - u32 max_size = args->return_size; - void __user *out = (void __user *) (uintptr_t) args->return_pointer; - int rc; - - if ((!max_size) || (!out)) - return -EINVAL; - - rc = hdev->asic_funcs->get_clk_rate(hdev, &clk_rate.cur_clk_rate_mhz, - &clk_rate.max_clk_rate_mhz); - if (rc) - return rc; - - return copy_to_user(out, &clk_rate, - min((size_t) max_size, sizeof(clk_rate))) ? -EFAULT : 0; -} - -static int get_reset_count(struct hl_device *hdev, struct hl_info_args *args) -{ - struct hl_info_reset_count reset_count = {0}; - u32 max_size = args->return_size; - void __user *out = (void __user *) (uintptr_t) args->return_pointer; - - if ((!max_size) || (!out)) - return -EINVAL; - - reset_count.hard_reset_cnt = hdev->hard_reset_cnt; - reset_count.soft_reset_cnt = hdev->soft_reset_cnt; - - return copy_to_user(out, &reset_count, - min((size_t) max_size, sizeof(reset_count))) ? -EFAULT : 0; -} - -static int time_sync_info(struct hl_device *hdev, struct hl_info_args *args) -{ - struct hl_info_time_sync time_sync = {0}; - u32 max_size = args->return_size; - void __user *out = (void __user *) (uintptr_t) args->return_pointer; - - if ((!max_size) || (!out)) - return -EINVAL; - - time_sync.device_time = hdev->asic_funcs->get_device_time(hdev); - time_sync.host_time = ktime_get_raw_ns(); - - return copy_to_user(out, &time_sync, - min((size_t) max_size, sizeof(time_sync))) ? -EFAULT : 0; -} - -static int cs_counters_info(struct hl_fpriv *hpriv, struct hl_info_args *args) -{ - struct hl_device *hdev = hpriv->hdev; - struct hl_info_cs_counters cs_counters = {0}; - u32 max_size = args->return_size; - void __user *out = (void __user *) (uintptr_t) args->return_pointer; - - if ((!max_size) || (!out)) - return -EINVAL; - - memcpy(&cs_counters.cs_counters, &hdev->aggregated_cs_counters, - sizeof(struct hl_cs_counters)); - - if (hpriv->ctx) - memcpy(&cs_counters.ctx_cs_counters, &hpriv->ctx->cs_counters, - sizeof(struct hl_cs_counters)); - - return copy_to_user(out, &cs_counters, - min((size_t) max_size, sizeof(cs_counters))) ? -EFAULT : 0; -} - -static int _hl_info_ioctl(struct hl_fpriv *hpriv, void *data, - struct device *dev) -{ - struct hl_info_args *args = data; - struct hl_device *hdev = hpriv->hdev; - int rc; - - /* - * Information is returned for the following opcodes even if the device - * is disabled or in reset. - */ - switch (args->op) { - case HL_INFO_HW_IP_INFO: - return hw_ip_info(hdev, args); - - case HL_INFO_DEVICE_STATUS: - return device_status_info(hdev, args); - - case HL_INFO_RESET_COUNT: - return get_reset_count(hdev, args); - - default: - break; - } - - if (hl_device_disabled_or_in_reset(hdev)) { - dev_warn_ratelimited(dev, - "Device is %s. Can't execute INFO IOCTL\n", - atomic_read(&hdev->in_reset) ? "in_reset" : "disabled"); - return -EBUSY; - } - - switch (args->op) { - case HL_INFO_HW_EVENTS: - rc = hw_events_info(hdev, false, args); - break; - - case HL_INFO_DRAM_USAGE: - rc = dram_usage_info(hpriv, args); - break; - - case HL_INFO_HW_IDLE: - rc = hw_idle(hdev, args); - break; - - case HL_INFO_DEVICE_UTILIZATION: - rc = device_utilization(hdev, args); - break; - - case HL_INFO_HW_EVENTS_AGGREGATE: - rc = hw_events_info(hdev, true, args); - break; - - case HL_INFO_CLK_RATE: - rc = get_clk_rate(hdev, args); - break; - - case HL_INFO_TIME_SYNC: - return time_sync_info(hdev, args); - - case HL_INFO_CS_COUNTERS: - return cs_counters_info(hpriv, args); - - default: - dev_err(dev, "Invalid request %d\n", args->op); - rc = -ENOTTY; - break; - } - - return rc; -} - -static int hl_info_ioctl(struct hl_fpriv *hpriv, void *data) -{ - return _hl_info_ioctl(hpriv, data, hpriv->hdev->dev); -} - -static int hl_info_ioctl_control(struct hl_fpriv *hpriv, void *data) -{ - return _hl_info_ioctl(hpriv, data, hpriv->hdev->dev_ctrl); -} - -static int hl_debug_ioctl(struct hl_fpriv *hpriv, void *data) -{ - struct hl_debug_args *args = data; - struct hl_device *hdev = hpriv->hdev; - int rc = 0; - - if (hl_device_disabled_or_in_reset(hdev)) { - dev_warn_ratelimited(hdev->dev, - "Device is %s. Can't execute DEBUG IOCTL\n", - atomic_read(&hdev->in_reset) ? "in_reset" : "disabled"); - return -EBUSY; - } - - switch (args->op) { - case HL_DEBUG_OP_ETR: - case HL_DEBUG_OP_ETF: - case HL_DEBUG_OP_STM: - case HL_DEBUG_OP_FUNNEL: - case HL_DEBUG_OP_BMON: - case HL_DEBUG_OP_SPMU: - case HL_DEBUG_OP_TIMESTAMP: - if (!hdev->in_debug) { - dev_err_ratelimited(hdev->dev, - "Rejecting debug configuration request because device not in debug mode\n"); - return -EFAULT; - } - args->input_size = - min(args->input_size, hl_debug_struct_size[args->op]); - rc = debug_coresight(hdev, args); - break; - case HL_DEBUG_OP_SET_MODE: - rc = hl_device_set_debug_mode(hdev, (bool) args->enable); - break; - default: - dev_err(hdev->dev, "Invalid request %d\n", args->op); - rc = -ENOTTY; - break; - } - - return rc; -} - -#define HL_IOCTL_DEF(ioctl, _func) \ - [_IOC_NR(ioctl)] = {.cmd = ioctl, .func = _func} - -static const struct hl_ioctl_desc hl_ioctls[] = { - HL_IOCTL_DEF(HL_IOCTL_INFO, hl_info_ioctl), - HL_IOCTL_DEF(HL_IOCTL_CB, hl_cb_ioctl), - HL_IOCTL_DEF(HL_IOCTL_CS, hl_cs_ioctl), - HL_IOCTL_DEF(HL_IOCTL_WAIT_CS, hl_cs_wait_ioctl), - HL_IOCTL_DEF(HL_IOCTL_MEMORY, hl_mem_ioctl), - HL_IOCTL_DEF(HL_IOCTL_DEBUG, hl_debug_ioctl) -}; - -static const struct hl_ioctl_desc hl_ioctls_control[] = { - HL_IOCTL_DEF(HL_IOCTL_INFO, hl_info_ioctl_control) -}; - -static long _hl_ioctl(struct file *filep, unsigned int cmd, unsigned long arg, - const struct hl_ioctl_desc *ioctl, struct device *dev) -{ - struct hl_fpriv *hpriv = filep->private_data; - struct hl_device *hdev = hpriv->hdev; - unsigned int nr = _IOC_NR(cmd); - char stack_kdata[128] = {0}; - char *kdata = NULL; - unsigned int usize, asize; - hl_ioctl_t *func; - u32 hl_size; - int retcode; - - if (hdev->hard_reset_pending) { - dev_crit_ratelimited(hdev->dev_ctrl, - "Device HARD reset pending! Please close FD\n"); - return -ENODEV; - } - - /* Do not trust userspace, use our own definition */ - func = ioctl->func; - - if (unlikely(!func)) { - dev_dbg(dev, "no function\n"); - retcode = -ENOTTY; - goto out_err; - } - - hl_size = _IOC_SIZE(ioctl->cmd); - usize = asize = _IOC_SIZE(cmd); - if (hl_size > asize) - asize = hl_size; - - cmd = ioctl->cmd; - - if (cmd & (IOC_IN | IOC_OUT)) { - if (asize <= sizeof(stack_kdata)) { - kdata = stack_kdata; - } else { - kdata = kzalloc(asize, GFP_KERNEL); - if (!kdata) { - retcode = -ENOMEM; - goto out_err; - } - } - } - - if (cmd & IOC_IN) { - if (copy_from_user(kdata, (void __user *)arg, usize)) { - retcode = -EFAULT; - goto out_err; - } - } else if (cmd & IOC_OUT) { - memset(kdata, 0, usize); - } - - retcode = func(hpriv, kdata); - - if ((cmd & IOC_OUT) && copy_to_user((void __user *)arg, kdata, usize)) - retcode = -EFAULT; - -out_err: - if (retcode) - dev_dbg(dev, "error in ioctl: pid=%d, cmd=0x%02x, nr=0x%02x\n", - task_pid_nr(current), cmd, nr); - - if (kdata != stack_kdata) - kfree(kdata); - - return retcode; -} - -long hl_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) -{ - struct hl_fpriv *hpriv = filep->private_data; - struct hl_device *hdev = hpriv->hdev; - const struct hl_ioctl_desc *ioctl = NULL; - unsigned int nr = _IOC_NR(cmd); - - if ((nr >= HL_COMMAND_START) && (nr < HL_COMMAND_END)) { - ioctl = &hl_ioctls[nr]; - } else { - dev_err(hdev->dev, "invalid ioctl: pid=%d, nr=0x%02x\n", - task_pid_nr(current), nr); - return -ENOTTY; - } - - return _hl_ioctl(filep, cmd, arg, ioctl, hdev->dev); -} - -long hl_ioctl_control(struct file *filep, unsigned int cmd, unsigned long arg) -{ - struct hl_fpriv *hpriv = filep->private_data; - struct hl_device *hdev = hpriv->hdev; - const struct hl_ioctl_desc *ioctl = NULL; - unsigned int nr = _IOC_NR(cmd); - - if (nr == _IOC_NR(HL_IOCTL_INFO)) { - ioctl = &hl_ioctls_control[nr]; - } else { - dev_err(hdev->dev_ctrl, "invalid ioctl: pid=%d, nr=0x%02x\n", - task_pid_nr(current), nr); - return -ENOTTY; - } - - return _hl_ioctl(filep, cmd, arg, ioctl, hdev->dev_ctrl); -} diff --git a/drivers/misc/habanalabs/hw_queue.c b/drivers/misc/habanalabs/hw_queue.c deleted file mode 100644 index 287681646071..000000000000 --- a/drivers/misc/habanalabs/hw_queue.c +++ /dev/null @@ -1,918 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -/* - * Copyright 2016-2019 HabanaLabs, Ltd. - * All Rights Reserved. - */ - -#include "habanalabs.h" - -#include - -/* - * hl_queue_add_ptr - add to pi or ci and checks if it wraps around - * - * @ptr: the current pi/ci value - * @val: the amount to add - * - * Add val to ptr. It can go until twice the queue length. - */ -inline u32 hl_hw_queue_add_ptr(u32 ptr, u16 val) -{ - ptr += val; - ptr &= ((HL_QUEUE_LENGTH << 1) - 1); - return ptr; -} -static inline int queue_ci_get(atomic_t *ci, u32 queue_len) -{ - return atomic_read(ci) & ((queue_len << 1) - 1); -} - -static inline int queue_free_slots(struct hl_hw_queue *q, u32 queue_len) -{ - int delta = (q->pi - queue_ci_get(&q->ci, queue_len)); - - if (delta >= 0) - return (queue_len - delta); - else - return (abs(delta) - queue_len); -} - -void hl_int_hw_queue_update_ci(struct hl_cs *cs) -{ - struct hl_device *hdev = cs->ctx->hdev; - struct hl_hw_queue *q; - int i; - - if (hdev->disabled) - return; - - q = &hdev->kernel_queues[0]; - for (i = 0 ; i < hdev->asic_prop.max_queues ; i++, q++) { - if (q->queue_type == QUEUE_TYPE_INT) - atomic_add(cs->jobs_in_queue_cnt[i], &q->ci); - } -} - -/* - * ext_and_hw_queue_submit_bd() - Submit a buffer descriptor to an external or a - * H/W queue. - * @hdev: pointer to habanalabs device structure - * @q: pointer to habanalabs queue structure - * @ctl: BD's control word - * @len: BD's length - * @ptr: BD's pointer - * - * This function assumes there is enough space on the queue to submit a new - * BD to it. It initializes the next BD and calls the device specific - * function to set the pi (and doorbell) - * - * This function must be called when the scheduler mutex is taken - * - */ -static void ext_and_hw_queue_submit_bd(struct hl_device *hdev, - struct hl_hw_queue *q, u32 ctl, u32 len, u64 ptr) -{ - struct hl_bd *bd; - - bd = (struct hl_bd *) (uintptr_t) q->kernel_address; - bd += hl_pi_2_offset(q->pi); - bd->ctl = cpu_to_le32(ctl); - bd->len = cpu_to_le32(len); - bd->ptr = cpu_to_le64(ptr); - - q->pi = hl_queue_inc_ptr(q->pi); - hdev->asic_funcs->ring_doorbell(hdev, q->hw_queue_id, q->pi); -} - -/* - * ext_queue_sanity_checks - perform some sanity checks on external queue - * - * @hdev : pointer to hl_device structure - * @q : pointer to hl_hw_queue structure - * @num_of_entries : how many entries to check for space - * @reserve_cq_entry : whether to reserve an entry in the cq - * - * H/W queues spinlock should be taken before calling this function - * - * Perform the following: - * - Make sure we have enough space in the h/w queue - * - Make sure we have enough space in the completion queue - * - Reserve space in the completion queue (needs to be reversed if there - * is a failure down the road before the actual submission of work). Only - * do this action if reserve_cq_entry is true - * - */ -static int ext_queue_sanity_checks(struct hl_device *hdev, - struct hl_hw_queue *q, int num_of_entries, - bool reserve_cq_entry) -{ - atomic_t *free_slots = - &hdev->completion_queue[q->cq_id].free_slots_cnt; - int free_slots_cnt; - - /* Check we have enough space in the queue */ - free_slots_cnt = queue_free_slots(q, HL_QUEUE_LENGTH); - - if (free_slots_cnt < num_of_entries) { - dev_dbg(hdev->dev, "Queue %d doesn't have room for %d CBs\n", - q->hw_queue_id, num_of_entries); - return -EAGAIN; - } - - if (reserve_cq_entry) { - /* - * Check we have enough space in the completion queue - * Add -1 to counter (decrement) unless counter was already 0 - * In that case, CQ is full so we can't submit a new CB because - * we won't get ack on its completion - * atomic_add_unless will return 0 if counter was already 0 - */ - if (atomic_add_negative(num_of_entries * -1, free_slots)) { - dev_dbg(hdev->dev, "No space for %d on CQ %d\n", - num_of_entries, q->hw_queue_id); - atomic_add(num_of_entries, free_slots); - return -EAGAIN; - } - } - - return 0; -} - -/* - * int_queue_sanity_checks - perform some sanity checks on internal queue - * - * @hdev : pointer to hl_device structure - * @q : pointer to hl_hw_queue structure - * @num_of_entries : how many entries to check for space - * - * H/W queues spinlock should be taken before calling this function - * - * Perform the following: - * - Make sure we have enough space in the h/w queue - * - */ -static int int_queue_sanity_checks(struct hl_device *hdev, - struct hl_hw_queue *q, - int num_of_entries) -{ - int free_slots_cnt; - - if (num_of_entries > q->int_queue_len) { - dev_err(hdev->dev, - "Cannot populate queue %u with %u jobs\n", - q->hw_queue_id, num_of_entries); - return -ENOMEM; - } - - /* Check we have enough space in the queue */ - free_slots_cnt = queue_free_slots(q, q->int_queue_len); - - if (free_slots_cnt < num_of_entries) { - dev_dbg(hdev->dev, "Queue %d doesn't have room for %d CBs\n", - q->hw_queue_id, num_of_entries); - return -EAGAIN; - } - - return 0; -} - -/* - * hw_queue_sanity_checks() - Make sure we have enough space in the h/w queue - * @hdev: Pointer to hl_device structure. - * @q: Pointer to hl_hw_queue structure. - * @num_of_entries: How many entries to check for space. - * - * Notice: We do not reserve queue entries so this function mustn't be called - * more than once per CS for the same queue - * - */ -static int hw_queue_sanity_checks(struct hl_device *hdev, struct hl_hw_queue *q, - int num_of_entries) -{ - int free_slots_cnt; - - /* Check we have enough space in the queue */ - free_slots_cnt = queue_free_slots(q, HL_QUEUE_LENGTH); - - if (free_slots_cnt < num_of_entries) { - dev_dbg(hdev->dev, "Queue %d doesn't have room for %d CBs\n", - q->hw_queue_id, num_of_entries); - return -EAGAIN; - } - - return 0; -} - -/* - * hl_hw_queue_send_cb_no_cmpl - send a single CB (not a JOB) without completion - * - * @hdev: pointer to hl_device structure - * @hw_queue_id: Queue's type - * @cb_size: size of CB - * @cb_ptr: pointer to CB location - * - * This function sends a single CB, that must NOT generate a completion entry - * - */ -int hl_hw_queue_send_cb_no_cmpl(struct hl_device *hdev, u32 hw_queue_id, - u32 cb_size, u64 cb_ptr) -{ - struct hl_hw_queue *q = &hdev->kernel_queues[hw_queue_id]; - int rc = 0; - - /* - * The CPU queue is a synchronous queue with an effective depth of - * a single entry (although it is allocated with room for multiple - * entries). Therefore, there is a different lock, called - * send_cpu_message_lock, that serializes accesses to the CPU queue. - * As a result, we don't need to lock the access to the entire H/W - * queues module when submitting a JOB to the CPU queue - */ - if (q->queue_type != QUEUE_TYPE_CPU) - hdev->asic_funcs->hw_queues_lock(hdev); - - if (hdev->disabled) { - rc = -EPERM; - goto out; - } - - /* - * hl_hw_queue_send_cb_no_cmpl() is called for queues of a H/W queue - * type only on init phase, when the queues are empty and being tested, - * so there is no need for sanity checks. - */ - if (q->queue_type != QUEUE_TYPE_HW) { - rc = ext_queue_sanity_checks(hdev, q, 1, false); - if (rc) - goto out; - } - - ext_and_hw_queue_submit_bd(hdev, q, 0, cb_size, cb_ptr); - -out: - if (q->queue_type != QUEUE_TYPE_CPU) - hdev->asic_funcs->hw_queues_unlock(hdev); - - return rc; -} - -/* - * ext_queue_schedule_job - submit a JOB to an external queue - * - * @job: pointer to the job that needs to be submitted to the queue - * - * This function must be called when the scheduler mutex is taken - * - */ -static void ext_queue_schedule_job(struct hl_cs_job *job) -{ - struct hl_device *hdev = job->cs->ctx->hdev; - struct hl_hw_queue *q = &hdev->kernel_queues[job->hw_queue_id]; - struct hl_cq_entry cq_pkt; - struct hl_cq *cq; - u64 cq_addr; - struct hl_cb *cb; - u32 ctl; - u32 len; - u64 ptr; - - /* - * Update the JOB ID inside the BD CTL so the device would know what - * to write in the completion queue - */ - ctl = ((q->pi << BD_CTL_SHADOW_INDEX_SHIFT) & BD_CTL_SHADOW_INDEX_MASK); - - cb = job->patched_cb; - len = job->job_cb_size; - ptr = cb->bus_address; - - cq_pkt.data = cpu_to_le32( - ((q->pi << CQ_ENTRY_SHADOW_INDEX_SHIFT) - & CQ_ENTRY_SHADOW_INDEX_MASK) | - (1 << CQ_ENTRY_SHADOW_INDEX_VALID_SHIFT) | - (1 << CQ_ENTRY_READY_SHIFT)); - - /* - * No need to protect pi_offset because scheduling to the - * H/W queues is done under the scheduler mutex - * - * No need to check if CQ is full because it was already - * checked in ext_queue_sanity_checks - */ - cq = &hdev->completion_queue[q->cq_id]; - cq_addr = cq->bus_address + cq->pi * sizeof(struct hl_cq_entry); - - hdev->asic_funcs->add_end_of_cb_packets(hdev, cb->kernel_address, len, - cq_addr, - le32_to_cpu(cq_pkt.data), - q->msi_vec, - job->contains_dma_pkt); - - q->shadow_queue[hl_pi_2_offset(q->pi)] = job; - - cq->pi = hl_cq_inc_ptr(cq->pi); - - ext_and_hw_queue_submit_bd(hdev, q, ctl, len, ptr); -} - -/* - * int_queue_schedule_job - submit a JOB to an internal queue - * - * @job: pointer to the job that needs to be submitted to the queue - * - * This function must be called when the scheduler mutex is taken - * - */ -static void int_queue_schedule_job(struct hl_cs_job *job) -{ - struct hl_device *hdev = job->cs->ctx->hdev; - struct hl_hw_queue *q = &hdev->kernel_queues[job->hw_queue_id]; - struct hl_bd bd; - __le64 *pi; - - bd.ctl = 0; - bd.len = cpu_to_le32(job->job_cb_size); - bd.ptr = cpu_to_le64((u64) (uintptr_t) job->user_cb); - - pi = (__le64 *) (uintptr_t) (q->kernel_address + - ((q->pi & (q->int_queue_len - 1)) * sizeof(bd))); - - q->pi++; - q->pi &= ((q->int_queue_len << 1) - 1); - - hdev->asic_funcs->pqe_write(hdev, pi, &bd); - - hdev->asic_funcs->ring_doorbell(hdev, q->hw_queue_id, q->pi); -} - -/* - * hw_queue_schedule_job - submit a JOB to a H/W queue - * - * @job: pointer to the job that needs to be submitted to the queue - * - * This function must be called when the scheduler mutex is taken - * - */ -static void hw_queue_schedule_job(struct hl_cs_job *job) -{ - struct hl_device *hdev = job->cs->ctx->hdev; - struct hl_hw_queue *q = &hdev->kernel_queues[job->hw_queue_id]; - u64 ptr; - u32 offset, ctl, len; - - /* - * Upon PQE completion, COMP_DATA is used as the write data to the - * completion queue (QMAN HBW message), and COMP_OFFSET is used as the - * write address offset in the SM block (QMAN LBW message). - * The write address offset is calculated as "COMP_OFFSET << 2". - */ - offset = job->cs->sequence & (hdev->asic_prop.max_pending_cs - 1); - ctl = ((offset << BD_CTL_COMP_OFFSET_SHIFT) & BD_CTL_COMP_OFFSET_MASK) | - ((q->pi << BD_CTL_COMP_DATA_SHIFT) & BD_CTL_COMP_DATA_MASK); - - len = job->job_cb_size; - - /* - * A patched CB is created only if a user CB was allocated by driver and - * MMU is disabled. If MMU is enabled, the user CB should be used - * instead. If the user CB wasn't allocated by driver, assume that it - * holds an address. - */ - if (job->patched_cb) - ptr = job->patched_cb->bus_address; - else if (job->is_kernel_allocated_cb) - ptr = job->user_cb->bus_address; - else - ptr = (u64) (uintptr_t) job->user_cb; - - ext_and_hw_queue_submit_bd(hdev, q, ctl, len, ptr); -} - -/* - * init_signal_wait_cs - initialize a signal/wait CS - * @cs: pointer to the signal/wait CS - * - * H/W queues spinlock should be taken before calling this function - */ -static void init_signal_wait_cs(struct hl_cs *cs) -{ - struct hl_ctx *ctx = cs->ctx; - struct hl_device *hdev = ctx->hdev; - struct hl_hw_queue *hw_queue; - struct hl_cs_compl *cs_cmpl = - container_of(cs->fence, struct hl_cs_compl, base_fence); - - struct hl_hw_sob *hw_sob; - struct hl_cs_job *job; - u32 q_idx; - - /* There is only one job in a signal/wait CS */ - job = list_first_entry(&cs->job_list, struct hl_cs_job, - cs_node); - q_idx = job->hw_queue_id; - hw_queue = &hdev->kernel_queues[q_idx]; - - if (cs->type & CS_TYPE_SIGNAL) { - hw_sob = &hw_queue->hw_sob[hw_queue->curr_sob_offset]; - - cs_cmpl->hw_sob = hw_sob; - cs_cmpl->sob_val = hw_queue->next_sob_val++; - - dev_dbg(hdev->dev, - "generate signal CB, sob_id: %d, sob val: 0x%x, q_idx: %d\n", - cs_cmpl->hw_sob->sob_id, cs_cmpl->sob_val, q_idx); - - hdev->asic_funcs->gen_signal_cb(hdev, job->patched_cb, - cs_cmpl->hw_sob->sob_id); - - kref_get(&hw_sob->kref); - - /* check for wraparound */ - if (hw_queue->next_sob_val == HL_MAX_SOB_VAL) { - /* - * Decrement as we reached the max value. - * The release function won't be called here as we've - * just incremented the refcount. - */ - kref_put(&hw_sob->kref, hl_sob_reset_error); - hw_queue->next_sob_val = 1; - /* only two SOBs are currently in use */ - hw_queue->curr_sob_offset = - (hw_queue->curr_sob_offset + 1) % - HL_RSVD_SOBS_IN_USE; - - dev_dbg(hdev->dev, "switched to SOB %d, q_idx: %d\n", - hw_queue->curr_sob_offset, q_idx); - } - } else if (cs->type & CS_TYPE_WAIT) { - struct hl_cs_compl *signal_cs_cmpl; - - signal_cs_cmpl = container_of(cs->signal_fence, - struct hl_cs_compl, - base_fence); - - /* copy the the SOB id and value of the signal CS */ - cs_cmpl->hw_sob = signal_cs_cmpl->hw_sob; - cs_cmpl->sob_val = signal_cs_cmpl->sob_val; - - dev_dbg(hdev->dev, - "generate wait CB, sob_id: %d, sob_val: 0x%x, mon_id: %d, q_idx: %d\n", - cs_cmpl->hw_sob->sob_id, cs_cmpl->sob_val, - hw_queue->base_mon_id, q_idx); - - hdev->asic_funcs->gen_wait_cb(hdev, job->patched_cb, - cs_cmpl->hw_sob->sob_id, - cs_cmpl->sob_val, - hw_queue->base_mon_id, - q_idx); - - kref_get(&cs_cmpl->hw_sob->kref); - /* - * Must put the signal fence after the SOB refcnt increment so - * the SOB refcnt won't turn 0 and reset the SOB before the - * wait CS was submitted. - */ - mb(); - dma_fence_put(cs->signal_fence); - cs->signal_fence = NULL; - } -} - -/* - * hl_hw_queue_schedule_cs - schedule a command submission - * @cs: pointer to the CS - */ -int hl_hw_queue_schedule_cs(struct hl_cs *cs) -{ - struct hl_ctx *ctx = cs->ctx; - struct hl_device *hdev = ctx->hdev; - struct hl_cs_job *job, *tmp; - struct hl_hw_queue *q; - u32 max_queues; - int rc = 0, i, cq_cnt; - - hdev->asic_funcs->hw_queues_lock(hdev); - - if (hl_device_disabled_or_in_reset(hdev)) { - ctx->cs_counters.device_in_reset_drop_cnt++; - dev_err(hdev->dev, - "device is disabled or in reset, CS rejected!\n"); - rc = -EPERM; - goto out; - } - - max_queues = hdev->asic_prop.max_queues; - - q = &hdev->kernel_queues[0]; - for (i = 0, cq_cnt = 0 ; i < max_queues ; i++, q++) { - if (cs->jobs_in_queue_cnt[i]) { - switch (q->queue_type) { - case QUEUE_TYPE_EXT: - rc = ext_queue_sanity_checks(hdev, q, - cs->jobs_in_queue_cnt[i], true); - break; - case QUEUE_TYPE_INT: - rc = int_queue_sanity_checks(hdev, q, - cs->jobs_in_queue_cnt[i]); - break; - case QUEUE_TYPE_HW: - rc = hw_queue_sanity_checks(hdev, q, - cs->jobs_in_queue_cnt[i]); - break; - default: - dev_err(hdev->dev, "Queue type %d is invalid\n", - q->queue_type); - rc = -EINVAL; - break; - } - - if (rc) { - ctx->cs_counters.queue_full_drop_cnt++; - goto unroll_cq_resv; - } - - if (q->queue_type == QUEUE_TYPE_EXT) - cq_cnt++; - } - } - - if ((cs->type == CS_TYPE_SIGNAL) || (cs->type == CS_TYPE_WAIT)) - init_signal_wait_cs(cs); - - spin_lock(&hdev->hw_queues_mirror_lock); - list_add_tail(&cs->mirror_node, &hdev->hw_queues_mirror_list); - - /* Queue TDR if the CS is the first entry and if timeout is wanted */ - if ((hdev->timeout_jiffies != MAX_SCHEDULE_TIMEOUT) && - (list_first_entry(&hdev->hw_queues_mirror_list, - struct hl_cs, mirror_node) == cs)) { - cs->tdr_active = true; - schedule_delayed_work(&cs->work_tdr, hdev->timeout_jiffies); - spin_unlock(&hdev->hw_queues_mirror_lock); - } else { - spin_unlock(&hdev->hw_queues_mirror_lock); - } - - if (!hdev->cs_active_cnt++) { - struct hl_device_idle_busy_ts *ts; - - ts = &hdev->idle_busy_ts_arr[hdev->idle_busy_ts_idx]; - ts->busy_to_idle_ts = ktime_set(0, 0); - ts->idle_to_busy_ts = ktime_get(); - } - - list_for_each_entry_safe(job, tmp, &cs->job_list, cs_node) - switch (job->queue_type) { - case QUEUE_TYPE_EXT: - ext_queue_schedule_job(job); - break; - case QUEUE_TYPE_INT: - int_queue_schedule_job(job); - break; - case QUEUE_TYPE_HW: - hw_queue_schedule_job(job); - break; - default: - break; - } - - cs->submitted = true; - - goto out; - -unroll_cq_resv: - q = &hdev->kernel_queues[0]; - for (i = 0 ; (i < max_queues) && (cq_cnt > 0) ; i++, q++) { - if ((q->queue_type == QUEUE_TYPE_EXT) && - (cs->jobs_in_queue_cnt[i])) { - atomic_t *free_slots = - &hdev->completion_queue[i].free_slots_cnt; - atomic_add(cs->jobs_in_queue_cnt[i], free_slots); - cq_cnt--; - } - } - -out: - hdev->asic_funcs->hw_queues_unlock(hdev); - - return rc; -} - -/* - * hl_hw_queue_inc_ci_kernel - increment ci for kernel's queue - * - * @hdev: pointer to hl_device structure - * @hw_queue_id: which queue to increment its ci - */ -void hl_hw_queue_inc_ci_kernel(struct hl_device *hdev, u32 hw_queue_id) -{ - struct hl_hw_queue *q = &hdev->kernel_queues[hw_queue_id]; - - atomic_inc(&q->ci); -} - -static int ext_and_cpu_queue_init(struct hl_device *hdev, struct hl_hw_queue *q, - bool is_cpu_queue) -{ - void *p; - int rc; - - if (is_cpu_queue) - p = hdev->asic_funcs->cpu_accessible_dma_pool_alloc(hdev, - HL_QUEUE_SIZE_IN_BYTES, - &q->bus_address); - else - p = hdev->asic_funcs->asic_dma_alloc_coherent(hdev, - HL_QUEUE_SIZE_IN_BYTES, - &q->bus_address, - GFP_KERNEL | __GFP_ZERO); - if (!p) - return -ENOMEM; - - q->kernel_address = (u64) (uintptr_t) p; - - q->shadow_queue = kmalloc_array(HL_QUEUE_LENGTH, - sizeof(*q->shadow_queue), - GFP_KERNEL); - if (!q->shadow_queue) { - dev_err(hdev->dev, - "Failed to allocate shadow queue for H/W queue %d\n", - q->hw_queue_id); - rc = -ENOMEM; - goto free_queue; - } - - /* Make sure read/write pointers are initialized to start of queue */ - atomic_set(&q->ci, 0); - q->pi = 0; - - return 0; - -free_queue: - if (is_cpu_queue) - hdev->asic_funcs->cpu_accessible_dma_pool_free(hdev, - HL_QUEUE_SIZE_IN_BYTES, - (void *) (uintptr_t) q->kernel_address); - else - hdev->asic_funcs->asic_dma_free_coherent(hdev, - HL_QUEUE_SIZE_IN_BYTES, - (void *) (uintptr_t) q->kernel_address, - q->bus_address); - - return rc; -} - -static int int_queue_init(struct hl_device *hdev, struct hl_hw_queue *q) -{ - void *p; - - p = hdev->asic_funcs->get_int_queue_base(hdev, q->hw_queue_id, - &q->bus_address, &q->int_queue_len); - if (!p) { - dev_err(hdev->dev, - "Failed to get base address for internal queue %d\n", - q->hw_queue_id); - return -EFAULT; - } - - q->kernel_address = (u64) (uintptr_t) p; - q->pi = 0; - atomic_set(&q->ci, 0); - - return 0; -} - -static int cpu_queue_init(struct hl_device *hdev, struct hl_hw_queue *q) -{ - return ext_and_cpu_queue_init(hdev, q, true); -} - -static int ext_queue_init(struct hl_device *hdev, struct hl_hw_queue *q) -{ - return ext_and_cpu_queue_init(hdev, q, false); -} - -static int hw_queue_init(struct hl_device *hdev, struct hl_hw_queue *q) -{ - void *p; - - p = hdev->asic_funcs->asic_dma_alloc_coherent(hdev, - HL_QUEUE_SIZE_IN_BYTES, - &q->bus_address, - GFP_KERNEL | __GFP_ZERO); - if (!p) - return -ENOMEM; - - q->kernel_address = (u64) (uintptr_t) p; - - /* Make sure read/write pointers are initialized to start of queue */ - atomic_set(&q->ci, 0); - q->pi = 0; - - return 0; -} - -static void sync_stream_queue_init(struct hl_device *hdev, u32 q_idx) -{ - struct hl_hw_queue *hw_queue = &hdev->kernel_queues[q_idx]; - struct asic_fixed_properties *prop = &hdev->asic_prop; - struct hl_hw_sob *hw_sob; - int sob, queue_idx = hdev->sync_stream_queue_idx++; - - hw_queue->base_sob_id = - prop->sync_stream_first_sob + queue_idx * HL_RSVD_SOBS; - hw_queue->base_mon_id = - prop->sync_stream_first_mon + queue_idx * HL_RSVD_MONS; - hw_queue->next_sob_val = 1; - hw_queue->curr_sob_offset = 0; - - for (sob = 0 ; sob < HL_RSVD_SOBS ; sob++) { - hw_sob = &hw_queue->hw_sob[sob]; - hw_sob->hdev = hdev; - hw_sob->sob_id = hw_queue->base_sob_id + sob; - hw_sob->q_idx = q_idx; - kref_init(&hw_sob->kref); - } -} - -static void sync_stream_queue_reset(struct hl_device *hdev, u32 q_idx) -{ - struct hl_hw_queue *hw_queue = &hdev->kernel_queues[q_idx]; - - /* - * In case we got here due to a stuck CS, the refcnt might be bigger - * than 1 and therefore we reset it. - */ - kref_init(&hw_queue->hw_sob[hw_queue->curr_sob_offset].kref); - hw_queue->curr_sob_offset = 0; - hw_queue->next_sob_val = 1; -} - -/* - * queue_init - main initialization function for H/W queue object - * - * @hdev: pointer to hl_device device structure - * @q: pointer to hl_hw_queue queue structure - * @hw_queue_id: The id of the H/W queue - * - * Allocate dma-able memory for the queue and initialize fields - * Returns 0 on success - */ -static int queue_init(struct hl_device *hdev, struct hl_hw_queue *q, - u32 hw_queue_id) -{ - int rc; - - q->hw_queue_id = hw_queue_id; - - switch (q->queue_type) { - case QUEUE_TYPE_EXT: - rc = ext_queue_init(hdev, q); - break; - case QUEUE_TYPE_INT: - rc = int_queue_init(hdev, q); - break; - case QUEUE_TYPE_CPU: - rc = cpu_queue_init(hdev, q); - break; - case QUEUE_TYPE_HW: - rc = hw_queue_init(hdev, q); - break; - case QUEUE_TYPE_NA: - q->valid = 0; - return 0; - default: - dev_crit(hdev->dev, "wrong queue type %d during init\n", - q->queue_type); - rc = -EINVAL; - break; - } - - if (q->supports_sync_stream) - sync_stream_queue_init(hdev, q->hw_queue_id); - - if (rc) - return rc; - - q->valid = 1; - - return 0; -} - -/* - * hw_queue_fini - destroy queue - * - * @hdev: pointer to hl_device device structure - * @q: pointer to hl_hw_queue queue structure - * - * Free the queue memory - */ -static void queue_fini(struct hl_device *hdev, struct hl_hw_queue *q) -{ - if (!q->valid) - return; - - /* - * If we arrived here, there are no jobs waiting on this queue - * so we can safely remove it. - * This is because this function can only called when: - * 1. Either a context is deleted, which only can occur if all its - * jobs were finished - * 2. A context wasn't able to be created due to failure or timeout, - * which means there are no jobs on the queue yet - * - * The only exception are the queues of the kernel context, but - * if they are being destroyed, it means that the entire module is - * being removed. If the module is removed, it means there is no open - * user context. It also means that if a job was submitted by - * the kernel driver (e.g. context creation), the job itself was - * released by the kernel driver when a timeout occurred on its - * Completion. Thus, we don't need to release it again. - */ - - if (q->queue_type == QUEUE_TYPE_INT) - return; - - kfree(q->shadow_queue); - - if (q->queue_type == QUEUE_TYPE_CPU) - hdev->asic_funcs->cpu_accessible_dma_pool_free(hdev, - HL_QUEUE_SIZE_IN_BYTES, - (void *) (uintptr_t) q->kernel_address); - else - hdev->asic_funcs->asic_dma_free_coherent(hdev, - HL_QUEUE_SIZE_IN_BYTES, - (void *) (uintptr_t) q->kernel_address, - q->bus_address); -} - -int hl_hw_queues_create(struct hl_device *hdev) -{ - struct asic_fixed_properties *asic = &hdev->asic_prop; - struct hl_hw_queue *q; - int i, rc, q_ready_cnt; - - hdev->kernel_queues = kcalloc(asic->max_queues, - sizeof(*hdev->kernel_queues), GFP_KERNEL); - - if (!hdev->kernel_queues) { - dev_err(hdev->dev, "Not enough memory for H/W queues\n"); - return -ENOMEM; - } - - /* Initialize the H/W queues */ - for (i = 0, q_ready_cnt = 0, q = hdev->kernel_queues; - i < asic->max_queues ; i++, q_ready_cnt++, q++) { - - q->queue_type = asic->hw_queues_props[i].type; - q->supports_sync_stream = - asic->hw_queues_props[i].supports_sync_stream; - rc = queue_init(hdev, q, i); - if (rc) { - dev_err(hdev->dev, - "failed to initialize queue %d\n", i); - goto release_queues; - } - } - - return 0; - -release_queues: - for (i = 0, q = hdev->kernel_queues ; i < q_ready_cnt ; i++, q++) - queue_fini(hdev, q); - - kfree(hdev->kernel_queues); - - return rc; -} - -void hl_hw_queues_destroy(struct hl_device *hdev) -{ - struct hl_hw_queue *q; - u32 max_queues = hdev->asic_prop.max_queues; - int i; - - for (i = 0, q = hdev->kernel_queues ; i < max_queues ; i++, q++) - queue_fini(hdev, q); - - kfree(hdev->kernel_queues); -} - -void hl_hw_queue_reset(struct hl_device *hdev, bool hard_reset) -{ - struct hl_hw_queue *q; - u32 max_queues = hdev->asic_prop.max_queues; - int i; - - for (i = 0, q = hdev->kernel_queues ; i < max_queues ; i++, q++) { - if ((!q->valid) || - ((!hard_reset) && (q->queue_type == QUEUE_TYPE_CPU))) - continue; - q->pi = 0; - atomic_set(&q->ci, 0); - - if (q->supports_sync_stream) - sync_stream_queue_reset(hdev, q->hw_queue_id); - } -} diff --git a/drivers/misc/habanalabs/hwmon.c b/drivers/misc/habanalabs/hwmon.c deleted file mode 100644 index 8c6cd77e6af6..000000000000 --- a/drivers/misc/habanalabs/hwmon.c +++ /dev/null @@ -1,579 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -/* - * Copyright 2016-2019 HabanaLabs, Ltd. - * All Rights Reserved. - */ - -#include "habanalabs.h" - -#include -#include - -#define SENSORS_PKT_TIMEOUT 1000000 /* 1s */ -#define HWMON_NR_SENSOR_TYPES (hwmon_pwm + 1) - -int hl_build_hwmon_channel_info(struct hl_device *hdev, - struct armcp_sensor *sensors_arr) -{ - u32 counts[HWMON_NR_SENSOR_TYPES] = {0}; - u32 *sensors_by_type[HWMON_NR_SENSOR_TYPES] = {NULL}; - u32 sensors_by_type_next_index[HWMON_NR_SENSOR_TYPES] = {0}; - struct hwmon_channel_info **channels_info; - u32 num_sensors_for_type, num_active_sensor_types = 0, - arr_size = 0, *curr_arr; - enum hwmon_sensor_types type; - int rc, i, j; - - for (i = 0 ; i < ARMCP_MAX_SENSORS ; i++) { - type = le32_to_cpu(sensors_arr[i].type); - - if ((type == 0) && (sensors_arr[i].flags == 0)) - break; - - if (type >= HWMON_NR_SENSOR_TYPES) { - dev_err(hdev->dev, - "Got wrong sensor type %d from device\n", type); - return -EINVAL; - } - - counts[type]++; - arr_size++; - } - - for (i = 0 ; i < HWMON_NR_SENSOR_TYPES ; i++) { - if (counts[i] == 0) - continue; - - num_sensors_for_type = counts[i] + 1; - curr_arr = kcalloc(num_sensors_for_type, sizeof(*curr_arr), - GFP_KERNEL); - if (!curr_arr) { - rc = -ENOMEM; - goto sensors_type_err; - } - - num_active_sensor_types++; - sensors_by_type[i] = curr_arr; - } - - for (i = 0 ; i < arr_size ; i++) { - type = le32_to_cpu(sensors_arr[i].type); - curr_arr = sensors_by_type[type]; - curr_arr[sensors_by_type_next_index[type]++] = - le32_to_cpu(sensors_arr[i].flags); - } - - channels_info = kcalloc(num_active_sensor_types + 1, - sizeof(*channels_info), GFP_KERNEL); - if (!channels_info) { - rc = -ENOMEM; - goto channels_info_array_err; - } - - for (i = 0 ; i < num_active_sensor_types ; i++) { - channels_info[i] = kzalloc(sizeof(*channels_info[i]), - GFP_KERNEL); - if (!channels_info[i]) { - rc = -ENOMEM; - goto channel_info_err; - } - } - - for (i = 0, j = 0 ; i < HWMON_NR_SENSOR_TYPES ; i++) { - if (!sensors_by_type[i]) - continue; - - channels_info[j]->type = i; - channels_info[j]->config = sensors_by_type[i]; - j++; - } - - hdev->hl_chip_info->info = - (const struct hwmon_channel_info **)channels_info; - - return 0; - -channel_info_err: - for (i = 0 ; i < num_active_sensor_types ; i++) - if (channels_info[i]) { - kfree(channels_info[i]->config); - kfree(channels_info[i]); - } - kfree(channels_info); -channels_info_array_err: -sensors_type_err: - for (i = 0 ; i < HWMON_NR_SENSOR_TYPES ; i++) - kfree(sensors_by_type[i]); - - return rc; -} - -static int hl_read(struct device *dev, enum hwmon_sensor_types type, - u32 attr, int channel, long *val) -{ - struct hl_device *hdev = dev_get_drvdata(dev); - int rc; - - if (hl_device_disabled_or_in_reset(hdev)) - return -ENODEV; - - switch (type) { - case hwmon_temp: - switch (attr) { - case hwmon_temp_input: - case hwmon_temp_max: - case hwmon_temp_crit: - case hwmon_temp_max_hyst: - case hwmon_temp_crit_hyst: - case hwmon_temp_offset: - case hwmon_temp_highest: - break; - default: - return -EINVAL; - } - - rc = hl_get_temperature(hdev, channel, attr, val); - break; - case hwmon_in: - switch (attr) { - case hwmon_in_input: - case hwmon_in_min: - case hwmon_in_max: - case hwmon_in_highest: - break; - default: - return -EINVAL; - } - - rc = hl_get_voltage(hdev, channel, attr, val); - break; - case hwmon_curr: - switch (attr) { - case hwmon_curr_input: - case hwmon_curr_min: - case hwmon_curr_max: - case hwmon_curr_highest: - break; - default: - return -EINVAL; - } - - rc = hl_get_current(hdev, channel, attr, val); - break; - case hwmon_fan: - switch (attr) { - case hwmon_fan_input: - case hwmon_fan_min: - case hwmon_fan_max: - break; - default: - return -EINVAL; - } - rc = hl_get_fan_speed(hdev, channel, attr, val); - break; - case hwmon_pwm: - switch (attr) { - case hwmon_pwm_input: - case hwmon_pwm_enable: - break; - default: - return -EINVAL; - } - rc = hl_get_pwm_info(hdev, channel, attr, val); - break; - default: - return -EINVAL; - } - return rc; -} - -static int hl_write(struct device *dev, enum hwmon_sensor_types type, - u32 attr, int channel, long val) -{ - struct hl_device *hdev = dev_get_drvdata(dev); - - if (hl_device_disabled_or_in_reset(hdev)) - return -ENODEV; - - switch (type) { - case hwmon_temp: - switch (attr) { - case hwmon_temp_offset: - case hwmon_temp_reset_history: - break; - default: - return -EINVAL; - } - hl_set_temperature(hdev, channel, attr, val); - break; - case hwmon_pwm: - switch (attr) { - case hwmon_pwm_input: - case hwmon_pwm_enable: - break; - default: - return -EINVAL; - } - hl_set_pwm_info(hdev, channel, attr, val); - break; - case hwmon_in: - switch (attr) { - case hwmon_in_reset_history: - break; - default: - return -EINVAL; - } - hl_set_voltage(hdev, channel, attr, val); - break; - case hwmon_curr: - switch (attr) { - case hwmon_curr_reset_history: - break; - default: - return -EINVAL; - } - hl_set_current(hdev, channel, attr, val); - break; - default: - return -EINVAL; - } - return 0; -} - -static umode_t hl_is_visible(const void *data, enum hwmon_sensor_types type, - u32 attr, int channel) -{ - switch (type) { - case hwmon_temp: - switch (attr) { - case hwmon_temp_input: - case hwmon_temp_max: - case hwmon_temp_max_hyst: - case hwmon_temp_crit: - case hwmon_temp_crit_hyst: - case hwmon_temp_highest: - return 0444; - case hwmon_temp_offset: - return 0644; - case hwmon_temp_reset_history: - return 0200; - } - break; - case hwmon_in: - switch (attr) { - case hwmon_in_input: - case hwmon_in_min: - case hwmon_in_max: - case hwmon_in_highest: - return 0444; - case hwmon_in_reset_history: - return 0200; - } - break; - case hwmon_curr: - switch (attr) { - case hwmon_curr_input: - case hwmon_curr_min: - case hwmon_curr_max: - case hwmon_curr_highest: - return 0444; - case hwmon_curr_reset_history: - return 0200; - } - break; - case hwmon_fan: - switch (attr) { - case hwmon_fan_input: - case hwmon_fan_min: - case hwmon_fan_max: - return 0444; - } - break; - case hwmon_pwm: - switch (attr) { - case hwmon_pwm_input: - case hwmon_pwm_enable: - return 0644; - } - break; - default: - break; - } - return 0; -} - -static const struct hwmon_ops hl_hwmon_ops = { - .is_visible = hl_is_visible, - .read = hl_read, - .write = hl_write -}; - -int hl_get_temperature(struct hl_device *hdev, - int sensor_index, u32 attr, long *value) -{ - struct armcp_packet pkt; - int rc; - - memset(&pkt, 0, sizeof(pkt)); - - pkt.ctl = cpu_to_le32(ARMCP_PACKET_TEMPERATURE_GET << - ARMCP_PKT_CTL_OPCODE_SHIFT); - pkt.sensor_index = __cpu_to_le16(sensor_index); - pkt.type = __cpu_to_le16(attr); - - rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), - SENSORS_PKT_TIMEOUT, value); - - if (rc) { - dev_err(hdev->dev, - "Failed to get temperature from sensor %d, error %d\n", - sensor_index, rc); - *value = 0; - } - - return rc; -} - -int hl_set_temperature(struct hl_device *hdev, - int sensor_index, u32 attr, long value) -{ - struct armcp_packet pkt; - int rc; - - memset(&pkt, 0, sizeof(pkt)); - - pkt.ctl = cpu_to_le32(ARMCP_PACKET_TEMPERATURE_SET << - ARMCP_PKT_CTL_OPCODE_SHIFT); - pkt.sensor_index = __cpu_to_le16(sensor_index); - pkt.type = __cpu_to_le16(attr); - pkt.value = __cpu_to_le64(value); - - rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), - SENSORS_PKT_TIMEOUT, NULL); - - if (rc) - dev_err(hdev->dev, - "Failed to set temperature of sensor %d, error %d\n", - sensor_index, rc); - - return rc; -} - -int hl_get_voltage(struct hl_device *hdev, - int sensor_index, u32 attr, long *value) -{ - struct armcp_packet pkt; - int rc; - - memset(&pkt, 0, sizeof(pkt)); - - pkt.ctl = cpu_to_le32(ARMCP_PACKET_VOLTAGE_GET << - ARMCP_PKT_CTL_OPCODE_SHIFT); - pkt.sensor_index = __cpu_to_le16(sensor_index); - pkt.type = __cpu_to_le16(attr); - - rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), - SENSORS_PKT_TIMEOUT, value); - - if (rc) { - dev_err(hdev->dev, - "Failed to get voltage from sensor %d, error %d\n", - sensor_index, rc); - *value = 0; - } - - return rc; -} - -int hl_get_current(struct hl_device *hdev, - int sensor_index, u32 attr, long *value) -{ - struct armcp_packet pkt; - int rc; - - memset(&pkt, 0, sizeof(pkt)); - - pkt.ctl = cpu_to_le32(ARMCP_PACKET_CURRENT_GET << - ARMCP_PKT_CTL_OPCODE_SHIFT); - pkt.sensor_index = __cpu_to_le16(sensor_index); - pkt.type = __cpu_to_le16(attr); - - rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), - SENSORS_PKT_TIMEOUT, value); - - if (rc) { - dev_err(hdev->dev, - "Failed to get current from sensor %d, error %d\n", - sensor_index, rc); - *value = 0; - } - - return rc; -} - -int hl_get_fan_speed(struct hl_device *hdev, - int sensor_index, u32 attr, long *value) -{ - struct armcp_packet pkt; - int rc; - - memset(&pkt, 0, sizeof(pkt)); - - pkt.ctl = cpu_to_le32(ARMCP_PACKET_FAN_SPEED_GET << - ARMCP_PKT_CTL_OPCODE_SHIFT); - pkt.sensor_index = __cpu_to_le16(sensor_index); - pkt.type = __cpu_to_le16(attr); - - rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), - SENSORS_PKT_TIMEOUT, value); - - if (rc) { - dev_err(hdev->dev, - "Failed to get fan speed from sensor %d, error %d\n", - sensor_index, rc); - *value = 0; - } - - return rc; -} - -int hl_get_pwm_info(struct hl_device *hdev, - int sensor_index, u32 attr, long *value) -{ - struct armcp_packet pkt; - int rc; - - memset(&pkt, 0, sizeof(pkt)); - - pkt.ctl = cpu_to_le32(ARMCP_PACKET_PWM_GET << - ARMCP_PKT_CTL_OPCODE_SHIFT); - pkt.sensor_index = __cpu_to_le16(sensor_index); - pkt.type = __cpu_to_le16(attr); - - rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), - SENSORS_PKT_TIMEOUT, value); - - if (rc) { - dev_err(hdev->dev, - "Failed to get pwm info from sensor %d, error %d\n", - sensor_index, rc); - *value = 0; - } - - return rc; -} - -void hl_set_pwm_info(struct hl_device *hdev, int sensor_index, u32 attr, - long value) -{ - struct armcp_packet pkt; - int rc; - - memset(&pkt, 0, sizeof(pkt)); - - pkt.ctl = cpu_to_le32(ARMCP_PACKET_PWM_SET << - ARMCP_PKT_CTL_OPCODE_SHIFT); - pkt.sensor_index = __cpu_to_le16(sensor_index); - pkt.type = __cpu_to_le16(attr); - pkt.value = cpu_to_le64(value); - - rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), - SENSORS_PKT_TIMEOUT, NULL); - - if (rc) - dev_err(hdev->dev, - "Failed to set pwm info to sensor %d, error %d\n", - sensor_index, rc); -} - -int hl_set_voltage(struct hl_device *hdev, - int sensor_index, u32 attr, long value) -{ - struct armcp_packet pkt; - int rc; - - memset(&pkt, 0, sizeof(pkt)); - - pkt.ctl = cpu_to_le32(ARMCP_PACKET_VOLTAGE_SET << - ARMCP_PKT_CTL_OPCODE_SHIFT); - pkt.sensor_index = __cpu_to_le16(sensor_index); - pkt.type = __cpu_to_le16(attr); - pkt.value = __cpu_to_le64(value); - - rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), - SENSORS_PKT_TIMEOUT, NULL); - - if (rc) - dev_err(hdev->dev, - "Failed to set voltage of sensor %d, error %d\n", - sensor_index, rc); - - return rc; -} - -int hl_set_current(struct hl_device *hdev, - int sensor_index, u32 attr, long value) -{ - struct armcp_packet pkt; - int rc; - - memset(&pkt, 0, sizeof(pkt)); - - pkt.ctl = cpu_to_le32(ARMCP_PACKET_CURRENT_SET << - ARMCP_PKT_CTL_OPCODE_SHIFT); - pkt.sensor_index = __cpu_to_le16(sensor_index); - pkt.type = __cpu_to_le16(attr); - pkt.value = __cpu_to_le64(value); - - rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), - SENSORS_PKT_TIMEOUT, NULL); - - if (rc) - dev_err(hdev->dev, - "Failed to set current of sensor %d, error %d\n", - sensor_index, rc); - - return rc; -} - -int hl_hwmon_init(struct hl_device *hdev) -{ - struct device *dev = hdev->pdev ? &hdev->pdev->dev : hdev->dev; - struct asic_fixed_properties *prop = &hdev->asic_prop; - int rc; - - if ((hdev->hwmon_initialized) || !(hdev->fw_loading)) - return 0; - - if (hdev->hl_chip_info->info) { - hdev->hl_chip_info->ops = &hl_hwmon_ops; - - hdev->hwmon_dev = hwmon_device_register_with_info(dev, - prop->armcp_info.card_name, hdev, - hdev->hl_chip_info, NULL); - if (IS_ERR(hdev->hwmon_dev)) { - rc = PTR_ERR(hdev->hwmon_dev); - dev_err(hdev->dev, - "Unable to register hwmon device: %d\n", rc); - return rc; - } - - dev_info(hdev->dev, "%s: add sensors information\n", - dev_name(hdev->hwmon_dev)); - - hdev->hwmon_initialized = true; - } else { - dev_info(hdev->dev, "no available sensors\n"); - } - - return 0; -} - -void hl_hwmon_fini(struct hl_device *hdev) -{ - if (!hdev->hwmon_initialized) - return; - - hwmon_device_unregister(hdev->hwmon_dev); -} diff --git a/drivers/misc/habanalabs/include/armcp_if.h b/drivers/misc/habanalabs/include/armcp_if.h deleted file mode 100644 index 07f9972db28d..000000000000 --- a/drivers/misc/habanalabs/include/armcp_if.h +++ /dev/null @@ -1,407 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 - * - * Copyright 2016-2020 HabanaLabs, Ltd. - * All Rights Reserved. - * - */ - -#ifndef ARMCP_IF_H -#define ARMCP_IF_H - -#include - -/* - * EVENT QUEUE - */ - -struct hl_eq_header { - __le32 reserved; - __le32 ctl; -}; - -struct hl_eq_ecc_data { - __le64 ecc_address; - __le64 ecc_syndrom; - __u8 memory_wrapper_idx; - __u8 pad[7]; -}; - -struct hl_eq_entry { - struct hl_eq_header hdr; - union { - struct hl_eq_ecc_data ecc_data; - __le64 data[7]; - }; -}; - -#define HL_EQ_ENTRY_SIZE sizeof(struct hl_eq_entry) - -#define EQ_CTL_READY_SHIFT 31 -#define EQ_CTL_READY_MASK 0x80000000 - -#define EQ_CTL_EVENT_TYPE_SHIFT 16 -#define EQ_CTL_EVENT_TYPE_MASK 0x03FF0000 - -enum pq_init_status { - PQ_INIT_STATUS_NA = 0, - PQ_INIT_STATUS_READY_FOR_CP, - PQ_INIT_STATUS_READY_FOR_HOST, - PQ_INIT_STATUS_READY_FOR_CP_SINGLE_MSI -}; - -/* - * ArmCP Primary Queue Packets - * - * During normal operation, the host's kernel driver needs to send various - * messages to ArmCP, usually either to SET some value into a H/W periphery or - * to GET the current value of some H/W periphery. For example, SET the - * frequency of MME/TPC and GET the value of the thermal sensor. - * - * These messages can be initiated either by the User application or by the - * host's driver itself, e.g. power management code. In either case, the - * communication from the host's driver to ArmCP will *always* be in - * synchronous mode, meaning that the host will send a single message and poll - * until the message was acknowledged and the results are ready (if results are - * needed). - * - * This means that only a single message can be sent at a time and the host's - * driver must wait for its result before sending the next message. Having said - * that, because these are control messages which are sent in a relatively low - * frequency, this limitation seems acceptable. It's important to note that - * in case of multiple devices, messages to different devices *can* be sent - * at the same time. - * - * The message, inputs/outputs (if relevant) and fence object will be located - * on the device DDR at an address that will be determined by the host's driver. - * During device initialization phase, the host will pass to ArmCP that address. - * Most of the message types will contain inputs/outputs inside the message - * itself. The common part of each message will contain the opcode of the - * message (its type) and a field representing a fence object. - * - * When the host's driver wishes to send a message to ArmCP, it will write the - * message contents to the device DDR, clear the fence object and then write the - * value 484 to the mmGIC_DISTRIBUTOR__5_GICD_SETSPI_NSR register to issue - * the 484 interrupt-id to the ARM core. - * - * Upon receiving the 484 interrupt-id, ArmCP will read the message from the - * DDR. In case the message is a SET operation, ArmCP will first perform the - * operation and then write to the fence object on the device DDR. In case the - * message is a GET operation, ArmCP will first fill the results section on the - * device DDR and then write to the fence object. If an error occurred, ArmCP - * will fill the rc field with the right error code. - * - * In the meantime, the host's driver will poll on the fence object. Once the - * host sees that the fence object is signaled, it will read the results from - * the device DDR (if relevant) and resume the code execution in the host's - * driver. - * - * To use QMAN packets, the opcode must be the QMAN opcode, shifted by 8 - * so the value being put by the host's driver matches the value read by ArmCP - * - * Non-QMAN packets should be limited to values 1 through (2^8 - 1) - * - * Detailed description: - * - * ARMCP_PACKET_DISABLE_PCI_ACCESS - - * After receiving this packet the embedded CPU must NOT issue PCI - * transactions (read/write) towards the Host CPU. This also include - * sending MSI-X interrupts. - * This packet is usually sent before the device is moved to D3Hot state. - * - * ARMCP_PACKET_ENABLE_PCI_ACCESS - - * After receiving this packet the embedded CPU is allowed to issue PCI - * transactions towards the Host CPU, including sending MSI-X interrupts. - * This packet is usually send after the device is moved to D0 state. - * - * ARMCP_PACKET_TEMPERATURE_GET - - * Fetch the current temperature / Max / Max Hyst / Critical / - * Critical Hyst of a specified thermal sensor. The packet's - * arguments specify the desired sensor and the field to get. - * - * ARMCP_PACKET_VOLTAGE_GET - - * Fetch the voltage / Max / Min of a specified sensor. The packet's - * arguments specify the sensor and type. - * - * ARMCP_PACKET_CURRENT_GET - - * Fetch the current / Max / Min of a specified sensor. The packet's - * arguments specify the sensor and type. - * - * ARMCP_PACKET_FAN_SPEED_GET - - * Fetch the speed / Max / Min of a specified fan. The packet's - * arguments specify the sensor and type. - * - * ARMCP_PACKET_PWM_GET - - * Fetch the pwm value / mode of a specified pwm. The packet's - * arguments specify the sensor and type. - * - * ARMCP_PACKET_PWM_SET - - * Set the pwm value / mode of a specified pwm. The packet's - * arguments specify the sensor, type and value. - * - * ARMCP_PACKET_FREQUENCY_SET - - * Set the frequency of a specified PLL. The packet's arguments specify - * the PLL and the desired frequency. The actual frequency in the device - * might differ from the requested frequency. - * - * ARMCP_PACKET_FREQUENCY_GET - - * Fetch the frequency of a specified PLL. The packet's arguments specify - * the PLL. - * - * ARMCP_PACKET_LED_SET - - * Set the state of a specified led. The packet's arguments - * specify the led and the desired state. - * - * ARMCP_PACKET_I2C_WR - - * Write 32-bit value to I2C device. The packet's arguments specify the - * I2C bus, address and value. - * - * ARMCP_PACKET_I2C_RD - - * Read 32-bit value from I2C device. The packet's arguments specify the - * I2C bus and address. - * - * ARMCP_PACKET_INFO_GET - - * Fetch information from the device as specified in the packet's - * structure. The host's driver passes the max size it allows the ArmCP to - * write to the structure, to prevent data corruption in case of - * mismatched driver/FW versions. - * - * ARMCP_PACKET_FLASH_PROGRAM_REMOVED - this packet was removed - * - * ARMCP_PACKET_UNMASK_RAZWI_IRQ - - * Unmask the given IRQ. The IRQ number is specified in the value field. - * The packet is sent after receiving an interrupt and printing its - * relevant information. - * - * ARMCP_PACKET_UNMASK_RAZWI_IRQ_ARRAY - - * Unmask the given IRQs. The IRQs numbers are specified in an array right - * after the armcp_packet structure, where its first element is the array - * length. The packet is sent after a soft reset was done in order to - * handle any interrupts that were sent during the reset process. - * - * ARMCP_PACKET_TEST - - * Test packet for ArmCP connectivity. The CPU will put the fence value - * in the result field. - * - * ARMCP_PACKET_FREQUENCY_CURR_GET - - * Fetch the current frequency of a specified PLL. The packet's arguments - * specify the PLL. - * - * ARMCP_PACKET_MAX_POWER_GET - - * Fetch the maximal power of the device. - * - * ARMCP_PACKET_MAX_POWER_SET - - * Set the maximal power of the device. The packet's arguments specify - * the power. - * - * ARMCP_PACKET_EEPROM_DATA_GET - - * Get EEPROM data from the ArmCP kernel. The buffer is specified in the - * addr field. The CPU will put the returned data size in the result - * field. In addition, the host's driver passes the max size it allows the - * ArmCP to write to the structure, to prevent data corruption in case of - * mismatched driver/FW versions. - * - * ARMCP_PACKET_TEMPERATURE_SET - - * Set the value of the offset property of a specified thermal sensor. - * The packet's arguments specify the desired sensor and the field to - * set. - * - * ARMCP_PACKET_VOLTAGE_SET - - * Trigger the reset_history property of a specified voltage sensor. - * The packet's arguments specify the desired sensor and the field to - * set. - * - * ARMCP_PACKET_CURRENT_SET - - * Trigger the reset_history property of a specified current sensor. - * The packet's arguments specify the desired sensor and the field to - * set. - */ - -enum armcp_packet_id { - ARMCP_PACKET_DISABLE_PCI_ACCESS = 1, /* internal */ - ARMCP_PACKET_ENABLE_PCI_ACCESS, /* internal */ - ARMCP_PACKET_TEMPERATURE_GET, /* sysfs */ - ARMCP_PACKET_VOLTAGE_GET, /* sysfs */ - ARMCP_PACKET_CURRENT_GET, /* sysfs */ - ARMCP_PACKET_FAN_SPEED_GET, /* sysfs */ - ARMCP_PACKET_PWM_GET, /* sysfs */ - ARMCP_PACKET_PWM_SET, /* sysfs */ - ARMCP_PACKET_FREQUENCY_SET, /* sysfs */ - ARMCP_PACKET_FREQUENCY_GET, /* sysfs */ - ARMCP_PACKET_LED_SET, /* debugfs */ - ARMCP_PACKET_I2C_WR, /* debugfs */ - ARMCP_PACKET_I2C_RD, /* debugfs */ - ARMCP_PACKET_INFO_GET, /* IOCTL */ - ARMCP_PACKET_FLASH_PROGRAM_REMOVED, - ARMCP_PACKET_UNMASK_RAZWI_IRQ, /* internal */ - ARMCP_PACKET_UNMASK_RAZWI_IRQ_ARRAY, /* internal */ - ARMCP_PACKET_TEST, /* internal */ - ARMCP_PACKET_FREQUENCY_CURR_GET, /* sysfs */ - ARMCP_PACKET_MAX_POWER_GET, /* sysfs */ - ARMCP_PACKET_MAX_POWER_SET, /* sysfs */ - ARMCP_PACKET_EEPROM_DATA_GET, /* sysfs */ - ARMCP_RESERVED, - ARMCP_PACKET_TEMPERATURE_SET, /* sysfs */ - ARMCP_PACKET_VOLTAGE_SET, /* sysfs */ - ARMCP_PACKET_CURRENT_SET, /* sysfs */ -}; - -#define ARMCP_PACKET_FENCE_VAL 0xFE8CE7A5 - -#define ARMCP_PKT_CTL_RC_SHIFT 12 -#define ARMCP_PKT_CTL_RC_MASK 0x0000F000 - -#define ARMCP_PKT_CTL_OPCODE_SHIFT 16 -#define ARMCP_PKT_CTL_OPCODE_MASK 0x1FFF0000 - -struct armcp_packet { - union { - __le64 value; /* For SET packets */ - __le64 result; /* For GET packets */ - __le64 addr; /* For PQ */ - }; - - __le32 ctl; - - __le32 fence; /* Signal to host that message is completed */ - - union { - struct {/* For temperature/current/voltage/fan/pwm get/set */ - __le16 sensor_index; - __le16 type; - }; - - struct { /* For I2C read/write */ - __u8 i2c_bus; - __u8 i2c_addr; - __u8 i2c_reg; - __u8 pad; /* unused */ - }; - - /* For frequency get/set */ - __le32 pll_index; - - /* For led set */ - __le32 led_index; - - /* For get Armcp info/EEPROM data */ - __le32 data_max_size; - }; - - __le32 reserved; -}; - -struct armcp_unmask_irq_arr_packet { - struct armcp_packet armcp_pkt; - __le32 length; - __le32 irqs[0]; -}; - -enum armcp_packet_rc { - armcp_packet_success, - armcp_packet_invalid, - armcp_packet_fault -}; - -/* - * armcp_temp_type should adhere to hwmon_temp_attributes - * defined in Linux kernel hwmon.h file - */ -enum armcp_temp_type { - armcp_temp_input, - armcp_temp_max = 6, - armcp_temp_max_hyst, - armcp_temp_crit, - armcp_temp_crit_hyst, - armcp_temp_offset = 19, - armcp_temp_highest = 22, - armcp_temp_reset_history = 23 -}; - -enum armcp_in_attributes { - armcp_in_input, - armcp_in_min, - armcp_in_max, - armcp_in_highest = 7, - armcp_in_reset_history -}; - -enum armcp_curr_attributes { - armcp_curr_input, - armcp_curr_min, - armcp_curr_max, - armcp_curr_highest = 7, - armcp_curr_reset_history -}; - -enum armcp_fan_attributes { - armcp_fan_input, - armcp_fan_min = 2, - armcp_fan_max -}; - -enum armcp_pwm_attributes { - armcp_pwm_input, - armcp_pwm_enable -}; - -/* Event Queue Packets */ - -struct eq_generic_event { - __le64 data[7]; -}; - -/* - * ArmCP info - */ - -#define CARD_NAME_MAX_LEN 16 -#define VERSION_MAX_LEN 128 -#define ARMCP_MAX_SENSORS 128 - -struct armcp_sensor { - __le32 type; - __le32 flags; -}; - -/** - * struct armcp_card_types - ASIC card type. - * @armcp_card_type_pci: PCI card. - * @armcp_card_type_pmc: PCI Mezzanine Card. - */ -enum armcp_card_types { - armcp_card_type_pci, - armcp_card_type_pmc -}; - -/** - * struct armcp_info - Info from ArmCP that is necessary to the host's driver - * @sensors: available sensors description. - * @kernel_version: ArmCP linux kernel version. - * @reserved: reserved field. - * @card_type: card configuration type. - * @card_location: in a server, each card has different connections topology - * depending on its location (relevant for PMC card type) - * @cpld_version: CPLD programmed F/W version. - * @infineon_version: Infineon main DC-DC version. - * @fuse_version: silicon production FUSE information. - * @thermal_version: thermald S/W version. - * @armcp_version: ArmCP S/W version. - * @dram_size: available DRAM size. - * @card_name: card name that will be displayed in HWMON subsystem on the host - */ -struct armcp_info { - struct armcp_sensor sensors[ARMCP_MAX_SENSORS]; - __u8 kernel_version[VERSION_MAX_LEN]; - __le32 reserved; - __le32 card_type; - __le32 card_location; - __le32 cpld_version; - __le32 infineon_version; - __u8 fuse_version[VERSION_MAX_LEN]; - __u8 thermal_version[VERSION_MAX_LEN]; - __u8 armcp_version[VERSION_MAX_LEN]; - __le64 dram_size; - char card_name[CARD_NAME_MAX_LEN]; -}; - -#endif /* ARMCP_IF_H */ diff --git a/drivers/misc/habanalabs/include/common/armcp_if.h b/drivers/misc/habanalabs/include/common/armcp_if.h new file mode 100644 index 000000000000..07f9972db28d --- /dev/null +++ b/drivers/misc/habanalabs/include/common/armcp_if.h @@ -0,0 +1,407 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright 2016-2020 HabanaLabs, Ltd. + * All Rights Reserved. + * + */ + +#ifndef ARMCP_IF_H +#define ARMCP_IF_H + +#include + +/* + * EVENT QUEUE + */ + +struct hl_eq_header { + __le32 reserved; + __le32 ctl; +}; + +struct hl_eq_ecc_data { + __le64 ecc_address; + __le64 ecc_syndrom; + __u8 memory_wrapper_idx; + __u8 pad[7]; +}; + +struct hl_eq_entry { + struct hl_eq_header hdr; + union { + struct hl_eq_ecc_data ecc_data; + __le64 data[7]; + }; +}; + +#define HL_EQ_ENTRY_SIZE sizeof(struct hl_eq_entry) + +#define EQ_CTL_READY_SHIFT 31 +#define EQ_CTL_READY_MASK 0x80000000 + +#define EQ_CTL_EVENT_TYPE_SHIFT 16 +#define EQ_CTL_EVENT_TYPE_MASK 0x03FF0000 + +enum pq_init_status { + PQ_INIT_STATUS_NA = 0, + PQ_INIT_STATUS_READY_FOR_CP, + PQ_INIT_STATUS_READY_FOR_HOST, + PQ_INIT_STATUS_READY_FOR_CP_SINGLE_MSI +}; + +/* + * ArmCP Primary Queue Packets + * + * During normal operation, the host's kernel driver needs to send various + * messages to ArmCP, usually either to SET some value into a H/W periphery or + * to GET the current value of some H/W periphery. For example, SET the + * frequency of MME/TPC and GET the value of the thermal sensor. + * + * These messages can be initiated either by the User application or by the + * host's driver itself, e.g. power management code. In either case, the + * communication from the host's driver to ArmCP will *always* be in + * synchronous mode, meaning that the host will send a single message and poll + * until the message was acknowledged and the results are ready (if results are + * needed). + * + * This means that only a single message can be sent at a time and the host's + * driver must wait for its result before sending the next message. Having said + * that, because these are control messages which are sent in a relatively low + * frequency, this limitation seems acceptable. It's important to note that + * in case of multiple devices, messages to different devices *can* be sent + * at the same time. + * + * The message, inputs/outputs (if relevant) and fence object will be located + * on the device DDR at an address that will be determined by the host's driver. + * During device initialization phase, the host will pass to ArmCP that address. + * Most of the message types will contain inputs/outputs inside the message + * itself. The common part of each message will contain the opcode of the + * message (its type) and a field representing a fence object. + * + * When the host's driver wishes to send a message to ArmCP, it will write the + * message contents to the device DDR, clear the fence object and then write the + * value 484 to the mmGIC_DISTRIBUTOR__5_GICD_SETSPI_NSR register to issue + * the 484 interrupt-id to the ARM core. + * + * Upon receiving the 484 interrupt-id, ArmCP will read the message from the + * DDR. In case the message is a SET operation, ArmCP will first perform the + * operation and then write to the fence object on the device DDR. In case the + * message is a GET operation, ArmCP will first fill the results section on the + * device DDR and then write to the fence object. If an error occurred, ArmCP + * will fill the rc field with the right error code. + * + * In the meantime, the host's driver will poll on the fence object. Once the + * host sees that the fence object is signaled, it will read the results from + * the device DDR (if relevant) and resume the code execution in the host's + * driver. + * + * To use QMAN packets, the opcode must be the QMAN opcode, shifted by 8 + * so the value being put by the host's driver matches the value read by ArmCP + * + * Non-QMAN packets should be limited to values 1 through (2^8 - 1) + * + * Detailed description: + * + * ARMCP_PACKET_DISABLE_PCI_ACCESS - + * After receiving this packet the embedded CPU must NOT issue PCI + * transactions (read/write) towards the Host CPU. This also include + * sending MSI-X interrupts. + * This packet is usually sent before the device is moved to D3Hot state. + * + * ARMCP_PACKET_ENABLE_PCI_ACCESS - + * After receiving this packet the embedded CPU is allowed to issue PCI + * transactions towards the Host CPU, including sending MSI-X interrupts. + * This packet is usually send after the device is moved to D0 state. + * + * ARMCP_PACKET_TEMPERATURE_GET - + * Fetch the current temperature / Max / Max Hyst / Critical / + * Critical Hyst of a specified thermal sensor. The packet's + * arguments specify the desired sensor and the field to get. + * + * ARMCP_PACKET_VOLTAGE_GET - + * Fetch the voltage / Max / Min of a specified sensor. The packet's + * arguments specify the sensor and type. + * + * ARMCP_PACKET_CURRENT_GET - + * Fetch the current / Max / Min of a specified sensor. The packet's + * arguments specify the sensor and type. + * + * ARMCP_PACKET_FAN_SPEED_GET - + * Fetch the speed / Max / Min of a specified fan. The packet's + * arguments specify the sensor and type. + * + * ARMCP_PACKET_PWM_GET - + * Fetch the pwm value / mode of a specified pwm. The packet's + * arguments specify the sensor and type. + * + * ARMCP_PACKET_PWM_SET - + * Set the pwm value / mode of a specified pwm. The packet's + * arguments specify the sensor, type and value. + * + * ARMCP_PACKET_FREQUENCY_SET - + * Set the frequency of a specified PLL. The packet's arguments specify + * the PLL and the desired frequency. The actual frequency in the device + * might differ from the requested frequency. + * + * ARMCP_PACKET_FREQUENCY_GET - + * Fetch the frequency of a specified PLL. The packet's arguments specify + * the PLL. + * + * ARMCP_PACKET_LED_SET - + * Set the state of a specified led. The packet's arguments + * specify the led and the desired state. + * + * ARMCP_PACKET_I2C_WR - + * Write 32-bit value to I2C device. The packet's arguments specify the + * I2C bus, address and value. + * + * ARMCP_PACKET_I2C_RD - + * Read 32-bit value from I2C device. The packet's arguments specify the + * I2C bus and address. + * + * ARMCP_PACKET_INFO_GET - + * Fetch information from the device as specified in the packet's + * structure. The host's driver passes the max size it allows the ArmCP to + * write to the structure, to prevent data corruption in case of + * mismatched driver/FW versions. + * + * ARMCP_PACKET_FLASH_PROGRAM_REMOVED - this packet was removed + * + * ARMCP_PACKET_UNMASK_RAZWI_IRQ - + * Unmask the given IRQ. The IRQ number is specified in the value field. + * The packet is sent after receiving an interrupt and printing its + * relevant information. + * + * ARMCP_PACKET_UNMASK_RAZWI_IRQ_ARRAY - + * Unmask the given IRQs. The IRQs numbers are specified in an array right + * after the armcp_packet structure, where its first element is the array + * length. The packet is sent after a soft reset was done in order to + * handle any interrupts that were sent during the reset process. + * + * ARMCP_PACKET_TEST - + * Test packet for ArmCP connectivity. The CPU will put the fence value + * in the result field. + * + * ARMCP_PACKET_FREQUENCY_CURR_GET - + * Fetch the current frequency of a specified PLL. The packet's arguments + * specify the PLL. + * + * ARMCP_PACKET_MAX_POWER_GET - + * Fetch the maximal power of the device. + * + * ARMCP_PACKET_MAX_POWER_SET - + * Set the maximal power of the device. The packet's arguments specify + * the power. + * + * ARMCP_PACKET_EEPROM_DATA_GET - + * Get EEPROM data from the ArmCP kernel. The buffer is specified in the + * addr field. The CPU will put the returned data size in the result + * field. In addition, the host's driver passes the max size it allows the + * ArmCP to write to the structure, to prevent data corruption in case of + * mismatched driver/FW versions. + * + * ARMCP_PACKET_TEMPERATURE_SET - + * Set the value of the offset property of a specified thermal sensor. + * The packet's arguments specify the desired sensor and the field to + * set. + * + * ARMCP_PACKET_VOLTAGE_SET - + * Trigger the reset_history property of a specified voltage sensor. + * The packet's arguments specify the desired sensor and the field to + * set. + * + * ARMCP_PACKET_CURRENT_SET - + * Trigger the reset_history property of a specified current sensor. + * The packet's arguments specify the desired sensor and the field to + * set. + */ + +enum armcp_packet_id { + ARMCP_PACKET_DISABLE_PCI_ACCESS = 1, /* internal */ + ARMCP_PACKET_ENABLE_PCI_ACCESS, /* internal */ + ARMCP_PACKET_TEMPERATURE_GET, /* sysfs */ + ARMCP_PACKET_VOLTAGE_GET, /* sysfs */ + ARMCP_PACKET_CURRENT_GET, /* sysfs */ + ARMCP_PACKET_FAN_SPEED_GET, /* sysfs */ + ARMCP_PACKET_PWM_GET, /* sysfs */ + ARMCP_PACKET_PWM_SET, /* sysfs */ + ARMCP_PACKET_FREQUENCY_SET, /* sysfs */ + ARMCP_PACKET_FREQUENCY_GET, /* sysfs */ + ARMCP_PACKET_LED_SET, /* debugfs */ + ARMCP_PACKET_I2C_WR, /* debugfs */ + ARMCP_PACKET_I2C_RD, /* debugfs */ + ARMCP_PACKET_INFO_GET, /* IOCTL */ + ARMCP_PACKET_FLASH_PROGRAM_REMOVED, + ARMCP_PACKET_UNMASK_RAZWI_IRQ, /* internal */ + ARMCP_PACKET_UNMASK_RAZWI_IRQ_ARRAY, /* internal */ + ARMCP_PACKET_TEST, /* internal */ + ARMCP_PACKET_FREQUENCY_CURR_GET, /* sysfs */ + ARMCP_PACKET_MAX_POWER_GET, /* sysfs */ + ARMCP_PACKET_MAX_POWER_SET, /* sysfs */ + ARMCP_PACKET_EEPROM_DATA_GET, /* sysfs */ + ARMCP_RESERVED, + ARMCP_PACKET_TEMPERATURE_SET, /* sysfs */ + ARMCP_PACKET_VOLTAGE_SET, /* sysfs */ + ARMCP_PACKET_CURRENT_SET, /* sysfs */ +}; + +#define ARMCP_PACKET_FENCE_VAL 0xFE8CE7A5 + +#define ARMCP_PKT_CTL_RC_SHIFT 12 +#define ARMCP_PKT_CTL_RC_MASK 0x0000F000 + +#define ARMCP_PKT_CTL_OPCODE_SHIFT 16 +#define ARMCP_PKT_CTL_OPCODE_MASK 0x1FFF0000 + +struct armcp_packet { + union { + __le64 value; /* For SET packets */ + __le64 result; /* For GET packets */ + __le64 addr; /* For PQ */ + }; + + __le32 ctl; + + __le32 fence; /* Signal to host that message is completed */ + + union { + struct {/* For temperature/current/voltage/fan/pwm get/set */ + __le16 sensor_index; + __le16 type; + }; + + struct { /* For I2C read/write */ + __u8 i2c_bus; + __u8 i2c_addr; + __u8 i2c_reg; + __u8 pad; /* unused */ + }; + + /* For frequency get/set */ + __le32 pll_index; + + /* For led set */ + __le32 led_index; + + /* For get Armcp info/EEPROM data */ + __le32 data_max_size; + }; + + __le32 reserved; +}; + +struct armcp_unmask_irq_arr_packet { + struct armcp_packet armcp_pkt; + __le32 length; + __le32 irqs[0]; +}; + +enum armcp_packet_rc { + armcp_packet_success, + armcp_packet_invalid, + armcp_packet_fault +}; + +/* + * armcp_temp_type should adhere to hwmon_temp_attributes + * defined in Linux kernel hwmon.h file + */ +enum armcp_temp_type { + armcp_temp_input, + armcp_temp_max = 6, + armcp_temp_max_hyst, + armcp_temp_crit, + armcp_temp_crit_hyst, + armcp_temp_offset = 19, + armcp_temp_highest = 22, + armcp_temp_reset_history = 23 +}; + +enum armcp_in_attributes { + armcp_in_input, + armcp_in_min, + armcp_in_max, + armcp_in_highest = 7, + armcp_in_reset_history +}; + +enum armcp_curr_attributes { + armcp_curr_input, + armcp_curr_min, + armcp_curr_max, + armcp_curr_highest = 7, + armcp_curr_reset_history +}; + +enum armcp_fan_attributes { + armcp_fan_input, + armcp_fan_min = 2, + armcp_fan_max +}; + +enum armcp_pwm_attributes { + armcp_pwm_input, + armcp_pwm_enable +}; + +/* Event Queue Packets */ + +struct eq_generic_event { + __le64 data[7]; +}; + +/* + * ArmCP info + */ + +#define CARD_NAME_MAX_LEN 16 +#define VERSION_MAX_LEN 128 +#define ARMCP_MAX_SENSORS 128 + +struct armcp_sensor { + __le32 type; + __le32 flags; +}; + +/** + * struct armcp_card_types - ASIC card type. + * @armcp_card_type_pci: PCI card. + * @armcp_card_type_pmc: PCI Mezzanine Card. + */ +enum armcp_card_types { + armcp_card_type_pci, + armcp_card_type_pmc +}; + +/** + * struct armcp_info - Info from ArmCP that is necessary to the host's driver + * @sensors: available sensors description. + * @kernel_version: ArmCP linux kernel version. + * @reserved: reserved field. + * @card_type: card configuration type. + * @card_location: in a server, each card has different connections topology + * depending on its location (relevant for PMC card type) + * @cpld_version: CPLD programmed F/W version. + * @infineon_version: Infineon main DC-DC version. + * @fuse_version: silicon production FUSE information. + * @thermal_version: thermald S/W version. + * @armcp_version: ArmCP S/W version. + * @dram_size: available DRAM size. + * @card_name: card name that will be displayed in HWMON subsystem on the host + */ +struct armcp_info { + struct armcp_sensor sensors[ARMCP_MAX_SENSORS]; + __u8 kernel_version[VERSION_MAX_LEN]; + __le32 reserved; + __le32 card_type; + __le32 card_location; + __le32 cpld_version; + __le32 infineon_version; + __u8 fuse_version[VERSION_MAX_LEN]; + __u8 thermal_version[VERSION_MAX_LEN]; + __u8 armcp_version[VERSION_MAX_LEN]; + __le64 dram_size; + char card_name[CARD_NAME_MAX_LEN]; +}; + +#endif /* ARMCP_IF_H */ diff --git a/drivers/misc/habanalabs/include/common/hl_boot_if.h b/drivers/misc/habanalabs/include/common/hl_boot_if.h new file mode 100644 index 000000000000..c22d134e73af --- /dev/null +++ b/drivers/misc/habanalabs/include/common/hl_boot_if.h @@ -0,0 +1,98 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright 2018-2020 HabanaLabs, Ltd. + * All Rights Reserved. + * + */ + +#ifndef HL_BOOT_IF_H +#define HL_BOOT_IF_H + +#define LKD_HARD_RESET_MAGIC 0xED7BD694 +#define HL_POWER9_HOST_MAGIC 0x1DA30009 + +#define BOOT_FIT_SRAM_OFFSET 0x200000 + +/* + * CPU error bits in BOOT_ERROR registers + * + * CPU_BOOT_ERR0_DRAM_INIT_FAIL DRAM initialization failed. + * DRAM is not reliable to use. + * + * CPU_BOOT_ERR0_FIT_CORRUPTED FIT data integrity verification of the + * image provided by the host has failed. + * + * CPU_BOOT_ERR0_TS_INIT_FAIL Thermal Sensor initialization failed. + * Boot continues as usual, but keep in + * mind this is a warning. + * + * CPU_BOOT_ERR0_DRAM_SKIPPED DRAM initialization has been skipped. + * Skipping DRAM initialization has been + * requested (e.g. strap, command, etc.) + * and FW skipped the DRAM initialization. + * Host can initialize the DRAM. + * + * CPU_BOOT_ERR0_BMC_WAIT_SKIPPED Waiting for BMC data will be skipped. + * Meaning the BMC data might not be + * available until reset. + * + * CPU_BOOT_ERR0_NIC_DATA_NOT_RDY NIC data from BMC is not ready. + * BMC has not provided the NIC data yet. + * Once provided this bit will be cleared. + * + * CPU_BOOT_ERR0_NIC_FW_FAIL NIC FW loading failed. + * The NIC FW loading and initialization + * failed. This means NICs are not usable. + * + * CPU_BOOT_ERR0_ENABLED Error registers enabled. + * This is a main indication that the + * running FW populates the error + * registers. Meaning the error bits are + * not garbage, but actual error statuses. + */ +#define CPU_BOOT_ERR0_DRAM_INIT_FAIL (1 << 0) +#define CPU_BOOT_ERR0_FIT_CORRUPTED (1 << 1) +#define CPU_BOOT_ERR0_TS_INIT_FAIL (1 << 2) +#define CPU_BOOT_ERR0_DRAM_SKIPPED (1 << 3) +#define CPU_BOOT_ERR0_BMC_WAIT_SKIPPED (1 << 4) +#define CPU_BOOT_ERR0_NIC_DATA_NOT_RDY (1 << 5) +#define CPU_BOOT_ERR0_NIC_FW_FAIL (1 << 6) +#define CPU_BOOT_ERR0_ENABLED (1 << 31) + +enum cpu_boot_status { + CPU_BOOT_STATUS_NA = 0, /* Default value after reset of chip */ + CPU_BOOT_STATUS_IN_WFE = 1, + CPU_BOOT_STATUS_DRAM_RDY = 2, + CPU_BOOT_STATUS_SRAM_AVAIL = 3, + CPU_BOOT_STATUS_IN_BTL = 4, /* BTL is H/W FSM */ + CPU_BOOT_STATUS_IN_PREBOOT = 5, + CPU_BOOT_STATUS_IN_SPL, /* deprecated - not reported */ + CPU_BOOT_STATUS_IN_UBOOT = 7, + CPU_BOOT_STATUS_DRAM_INIT_FAIL, /* deprecated - will be removed */ + CPU_BOOT_STATUS_FIT_CORRUPTED, /* deprecated - will be removed */ + /* U-Boot console prompt activated, commands are not processed */ + CPU_BOOT_STATUS_UBOOT_NOT_READY = 10, + /* Finished NICs init, reported after DRAM and NICs */ + CPU_BOOT_STATUS_NIC_FW_RDY = 11, + CPU_BOOT_STATUS_TS_INIT_FAIL, /* deprecated - will be removed */ + CPU_BOOT_STATUS_DRAM_SKIPPED, /* deprecated - will be removed */ + CPU_BOOT_STATUS_BMC_WAITING_SKIPPED, /* deprecated - will be removed */ + /* Last boot loader progress status, ready to receive commands */ + CPU_BOOT_STATUS_READY_TO_BOOT = 15, + CPU_BOOT_STATUS_WAITING_FOR_BOOT_FIT = 16, +}; + +enum kmd_msg { + KMD_MSG_NA = 0, + KMD_MSG_GOTO_WFE, + KMD_MSG_FIT_RDY, + KMD_MSG_SKIP_BMC, +}; + +enum cpu_msg_status { + CPU_MSG_CLR = 0, + CPU_MSG_OK, + CPU_MSG_ERR, +}; + +#endif /* HL_BOOT_IF_H */ diff --git a/drivers/misc/habanalabs/include/common/qman_if.h b/drivers/misc/habanalabs/include/common/qman_if.h new file mode 100644 index 000000000000..0fdb49188ed7 --- /dev/null +++ b/drivers/misc/habanalabs/include/common/qman_if.h @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright 2016-2018 HabanaLabs, Ltd. + * All Rights Reserved. + * + */ + +#ifndef QMAN_IF_H +#define QMAN_IF_H + +#include + +/* + * PRIMARY QUEUE + */ + +struct hl_bd { + __le64 ptr; + __le32 len; + __le32 ctl; +}; + +#define HL_BD_SIZE sizeof(struct hl_bd) + +/* + * S/W CTL FIELDS. + * + * BD_CTL_REPEAT_VALID tells the CP whether the repeat field in the BD CTL is + * valid. 1 means the repeat field is valid, 0 means not-valid, + * i.e. repeat == 1 + */ +#define BD_CTL_REPEAT_VALID_SHIFT 24 +#define BD_CTL_REPEAT_VALID_MASK 0x01000000 + +#define BD_CTL_SHADOW_INDEX_SHIFT 0 +#define BD_CTL_SHADOW_INDEX_MASK 0x00000FFF + +/* + * H/W CTL FIELDS + */ + +#define BD_CTL_COMP_OFFSET_SHIFT 16 +#define BD_CTL_COMP_OFFSET_MASK 0x00FF0000 + +#define BD_CTL_COMP_DATA_SHIFT 0 +#define BD_CTL_COMP_DATA_MASK 0x0000FFFF + +/* + * COMPLETION QUEUE + */ + +struct hl_cq_entry { + __le32 data; +}; + +#define HL_CQ_ENTRY_SIZE sizeof(struct hl_cq_entry) + +#define CQ_ENTRY_READY_SHIFT 31 +#define CQ_ENTRY_READY_MASK 0x80000000 + +#define CQ_ENTRY_SHADOW_INDEX_VALID_SHIFT 30 +#define CQ_ENTRY_SHADOW_INDEX_VALID_MASK 0x40000000 + +#define CQ_ENTRY_SHADOW_INDEX_SHIFT BD_CTL_SHADOW_INDEX_SHIFT +#define CQ_ENTRY_SHADOW_INDEX_MASK BD_CTL_SHADOW_INDEX_MASK + + +#endif /* QMAN_IF_H */ diff --git a/drivers/misc/habanalabs/include/hl_boot_if.h b/drivers/misc/habanalabs/include/hl_boot_if.h deleted file mode 100644 index c22d134e73af..000000000000 --- a/drivers/misc/habanalabs/include/hl_boot_if.h +++ /dev/null @@ -1,98 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 - * - * Copyright 2018-2020 HabanaLabs, Ltd. - * All Rights Reserved. - * - */ - -#ifndef HL_BOOT_IF_H -#define HL_BOOT_IF_H - -#define LKD_HARD_RESET_MAGIC 0xED7BD694 -#define HL_POWER9_HOST_MAGIC 0x1DA30009 - -#define BOOT_FIT_SRAM_OFFSET 0x200000 - -/* - * CPU error bits in BOOT_ERROR registers - * - * CPU_BOOT_ERR0_DRAM_INIT_FAIL DRAM initialization failed. - * DRAM is not reliable to use. - * - * CPU_BOOT_ERR0_FIT_CORRUPTED FIT data integrity verification of the - * image provided by the host has failed. - * - * CPU_BOOT_ERR0_TS_INIT_FAIL Thermal Sensor initialization failed. - * Boot continues as usual, but keep in - * mind this is a warning. - * - * CPU_BOOT_ERR0_DRAM_SKIPPED DRAM initialization has been skipped. - * Skipping DRAM initialization has been - * requested (e.g. strap, command, etc.) - * and FW skipped the DRAM initialization. - * Host can initialize the DRAM. - * - * CPU_BOOT_ERR0_BMC_WAIT_SKIPPED Waiting for BMC data will be skipped. - * Meaning the BMC data might not be - * available until reset. - * - * CPU_BOOT_ERR0_NIC_DATA_NOT_RDY NIC data from BMC is not ready. - * BMC has not provided the NIC data yet. - * Once provided this bit will be cleared. - * - * CPU_BOOT_ERR0_NIC_FW_FAIL NIC FW loading failed. - * The NIC FW loading and initialization - * failed. This means NICs are not usable. - * - * CPU_BOOT_ERR0_ENABLED Error registers enabled. - * This is a main indication that the - * running FW populates the error - * registers. Meaning the error bits are - * not garbage, but actual error statuses. - */ -#define CPU_BOOT_ERR0_DRAM_INIT_FAIL (1 << 0) -#define CPU_BOOT_ERR0_FIT_CORRUPTED (1 << 1) -#define CPU_BOOT_ERR0_TS_INIT_FAIL (1 << 2) -#define CPU_BOOT_ERR0_DRAM_SKIPPED (1 << 3) -#define CPU_BOOT_ERR0_BMC_WAIT_SKIPPED (1 << 4) -#define CPU_BOOT_ERR0_NIC_DATA_NOT_RDY (1 << 5) -#define CPU_BOOT_ERR0_NIC_FW_FAIL (1 << 6) -#define CPU_BOOT_ERR0_ENABLED (1 << 31) - -enum cpu_boot_status { - CPU_BOOT_STATUS_NA = 0, /* Default value after reset of chip */ - CPU_BOOT_STATUS_IN_WFE = 1, - CPU_BOOT_STATUS_DRAM_RDY = 2, - CPU_BOOT_STATUS_SRAM_AVAIL = 3, - CPU_BOOT_STATUS_IN_BTL = 4, /* BTL is H/W FSM */ - CPU_BOOT_STATUS_IN_PREBOOT = 5, - CPU_BOOT_STATUS_IN_SPL, /* deprecated - not reported */ - CPU_BOOT_STATUS_IN_UBOOT = 7, - CPU_BOOT_STATUS_DRAM_INIT_FAIL, /* deprecated - will be removed */ - CPU_BOOT_STATUS_FIT_CORRUPTED, /* deprecated - will be removed */ - /* U-Boot console prompt activated, commands are not processed */ - CPU_BOOT_STATUS_UBOOT_NOT_READY = 10, - /* Finished NICs init, reported after DRAM and NICs */ - CPU_BOOT_STATUS_NIC_FW_RDY = 11, - CPU_BOOT_STATUS_TS_INIT_FAIL, /* deprecated - will be removed */ - CPU_BOOT_STATUS_DRAM_SKIPPED, /* deprecated - will be removed */ - CPU_BOOT_STATUS_BMC_WAITING_SKIPPED, /* deprecated - will be removed */ - /* Last boot loader progress status, ready to receive commands */ - CPU_BOOT_STATUS_READY_TO_BOOT = 15, - CPU_BOOT_STATUS_WAITING_FOR_BOOT_FIT = 16, -}; - -enum kmd_msg { - KMD_MSG_NA = 0, - KMD_MSG_GOTO_WFE, - KMD_MSG_FIT_RDY, - KMD_MSG_SKIP_BMC, -}; - -enum cpu_msg_status { - CPU_MSG_CLR = 0, - CPU_MSG_OK, - CPU_MSG_ERR, -}; - -#endif /* HL_BOOT_IF_H */ diff --git a/drivers/misc/habanalabs/include/qman_if.h b/drivers/misc/habanalabs/include/qman_if.h deleted file mode 100644 index 0fdb49188ed7..000000000000 --- a/drivers/misc/habanalabs/include/qman_if.h +++ /dev/null @@ -1,68 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 - * - * Copyright 2016-2018 HabanaLabs, Ltd. - * All Rights Reserved. - * - */ - -#ifndef QMAN_IF_H -#define QMAN_IF_H - -#include - -/* - * PRIMARY QUEUE - */ - -struct hl_bd { - __le64 ptr; - __le32 len; - __le32 ctl; -}; - -#define HL_BD_SIZE sizeof(struct hl_bd) - -/* - * S/W CTL FIELDS. - * - * BD_CTL_REPEAT_VALID tells the CP whether the repeat field in the BD CTL is - * valid. 1 means the repeat field is valid, 0 means not-valid, - * i.e. repeat == 1 - */ -#define BD_CTL_REPEAT_VALID_SHIFT 24 -#define BD_CTL_REPEAT_VALID_MASK 0x01000000 - -#define BD_CTL_SHADOW_INDEX_SHIFT 0 -#define BD_CTL_SHADOW_INDEX_MASK 0x00000FFF - -/* - * H/W CTL FIELDS - */ - -#define BD_CTL_COMP_OFFSET_SHIFT 16 -#define BD_CTL_COMP_OFFSET_MASK 0x00FF0000 - -#define BD_CTL_COMP_DATA_SHIFT 0 -#define BD_CTL_COMP_DATA_MASK 0x0000FFFF - -/* - * COMPLETION QUEUE - */ - -struct hl_cq_entry { - __le32 data; -}; - -#define HL_CQ_ENTRY_SIZE sizeof(struct hl_cq_entry) - -#define CQ_ENTRY_READY_SHIFT 31 -#define CQ_ENTRY_READY_MASK 0x80000000 - -#define CQ_ENTRY_SHADOW_INDEX_VALID_SHIFT 30 -#define CQ_ENTRY_SHADOW_INDEX_VALID_MASK 0x40000000 - -#define CQ_ENTRY_SHADOW_INDEX_SHIFT BD_CTL_SHADOW_INDEX_SHIFT -#define CQ_ENTRY_SHADOW_INDEX_MASK BD_CTL_SHADOW_INDEX_MASK - - -#endif /* QMAN_IF_H */ diff --git a/drivers/misc/habanalabs/irq.c b/drivers/misc/habanalabs/irq.c deleted file mode 100644 index c8db717023f5..000000000000 --- a/drivers/misc/habanalabs/irq.c +++ /dev/null @@ -1,320 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -/* - * Copyright 2016-2019 HabanaLabs, Ltd. - * All Rights Reserved. - */ - -#include "habanalabs.h" - -#include - -/** - * struct hl_eqe_work - This structure is used to schedule work of EQ - * entry and armcp_reset event - * - * @eq_work: workqueue object to run when EQ entry is received - * @hdev: pointer to device structure - * @eq_entry: copy of the EQ entry - */ -struct hl_eqe_work { - struct work_struct eq_work; - struct hl_device *hdev; - struct hl_eq_entry eq_entry; -}; - -/** - * hl_cq_inc_ptr - increment ci or pi of cq - * - * @ptr: the current ci or pi value of the completion queue - * - * Increment ptr by 1. If it reaches the number of completion queue - * entries, set it to 0 - */ -inline u32 hl_cq_inc_ptr(u32 ptr) -{ - ptr++; - if (unlikely(ptr == HL_CQ_LENGTH)) - ptr = 0; - return ptr; -} - -/** - * hl_eq_inc_ptr - increment ci of eq - * - * @ptr: the current ci value of the event queue - * - * Increment ptr by 1. If it reaches the number of event queue - * entries, set it to 0 - */ -inline u32 hl_eq_inc_ptr(u32 ptr) -{ - ptr++; - if (unlikely(ptr == HL_EQ_LENGTH)) - ptr = 0; - return ptr; -} - -static void irq_handle_eqe(struct work_struct *work) -{ - struct hl_eqe_work *eqe_work = container_of(work, struct hl_eqe_work, - eq_work); - struct hl_device *hdev = eqe_work->hdev; - - hdev->asic_funcs->handle_eqe(hdev, &eqe_work->eq_entry); - - kfree(eqe_work); -} - -/** - * hl_irq_handler_cq - irq handler for completion queue - * - * @irq: irq number - * @arg: pointer to completion queue structure - * - */ -irqreturn_t hl_irq_handler_cq(int irq, void *arg) -{ - struct hl_cq *cq = arg; - struct hl_device *hdev = cq->hdev; - struct hl_hw_queue *queue; - struct hl_cs_job *job; - bool shadow_index_valid; - u16 shadow_index; - struct hl_cq_entry *cq_entry, *cq_base; - - if (hdev->disabled) { - dev_dbg(hdev->dev, - "Device disabled but received IRQ %d for CQ %d\n", - irq, cq->hw_queue_id); - return IRQ_HANDLED; - } - - cq_base = (struct hl_cq_entry *) (uintptr_t) cq->kernel_address; - - while (1) { - bool entry_ready = ((le32_to_cpu(cq_base[cq->ci].data) & - CQ_ENTRY_READY_MASK) - >> CQ_ENTRY_READY_SHIFT); - - if (!entry_ready) - break; - - cq_entry = (struct hl_cq_entry *) &cq_base[cq->ci]; - - /* Make sure we read CQ entry contents after we've - * checked the ownership bit. - */ - dma_rmb(); - - shadow_index_valid = ((le32_to_cpu(cq_entry->data) & - CQ_ENTRY_SHADOW_INDEX_VALID_MASK) - >> CQ_ENTRY_SHADOW_INDEX_VALID_SHIFT); - - shadow_index = (u16) ((le32_to_cpu(cq_entry->data) & - CQ_ENTRY_SHADOW_INDEX_MASK) - >> CQ_ENTRY_SHADOW_INDEX_SHIFT); - - queue = &hdev->kernel_queues[cq->hw_queue_id]; - - if ((shadow_index_valid) && (!hdev->disabled)) { - job = queue->shadow_queue[hl_pi_2_offset(shadow_index)]; - queue_work(hdev->cq_wq[cq->cq_idx], &job->finish_work); - } - - atomic_inc(&queue->ci); - - /* Clear CQ entry ready bit */ - cq_entry->data = cpu_to_le32(le32_to_cpu(cq_entry->data) & - ~CQ_ENTRY_READY_MASK); - - cq->ci = hl_cq_inc_ptr(cq->ci); - - /* Increment free slots */ - atomic_inc(&cq->free_slots_cnt); - } - - return IRQ_HANDLED; -} - -/** - * hl_irq_handler_eq - irq handler for event queue - * - * @irq: irq number - * @arg: pointer to event queue structure - * - */ -irqreturn_t hl_irq_handler_eq(int irq, void *arg) -{ - struct hl_eq *eq = arg; - struct hl_device *hdev = eq->hdev; - struct hl_eq_entry *eq_entry; - struct hl_eq_entry *eq_base; - struct hl_eqe_work *handle_eqe_work; - - eq_base = (struct hl_eq_entry *) (uintptr_t) eq->kernel_address; - - while (1) { - bool entry_ready = - ((le32_to_cpu(eq_base[eq->ci].hdr.ctl) & - EQ_CTL_READY_MASK) >> EQ_CTL_READY_SHIFT); - - if (!entry_ready) - break; - - eq_entry = &eq_base[eq->ci]; - - /* - * Make sure we read EQ entry contents after we've - * checked the ownership bit. - */ - dma_rmb(); - - if (hdev->disabled) { - dev_warn(hdev->dev, - "Device disabled but received IRQ %d for EQ\n", - irq); - goto skip_irq; - } - - handle_eqe_work = kmalloc(sizeof(*handle_eqe_work), GFP_ATOMIC); - if (handle_eqe_work) { - INIT_WORK(&handle_eqe_work->eq_work, irq_handle_eqe); - handle_eqe_work->hdev = hdev; - - memcpy(&handle_eqe_work->eq_entry, eq_entry, - sizeof(*eq_entry)); - - queue_work(hdev->eq_wq, &handle_eqe_work->eq_work); - } -skip_irq: - /* Clear EQ entry ready bit */ - eq_entry->hdr.ctl = - cpu_to_le32(le32_to_cpu(eq_entry->hdr.ctl) & - ~EQ_CTL_READY_MASK); - - eq->ci = hl_eq_inc_ptr(eq->ci); - - hdev->asic_funcs->update_eq_ci(hdev, eq->ci); - } - - return IRQ_HANDLED; -} - -/** - * hl_cq_init - main initialization function for an cq object - * - * @hdev: pointer to device structure - * @q: pointer to cq structure - * @hw_queue_id: The H/W queue ID this completion queue belongs to - * - * Allocate dma-able memory for the completion queue and initialize fields - * Returns 0 on success - */ -int hl_cq_init(struct hl_device *hdev, struct hl_cq *q, u32 hw_queue_id) -{ - void *p; - - p = hdev->asic_funcs->asic_dma_alloc_coherent(hdev, HL_CQ_SIZE_IN_BYTES, - &q->bus_address, GFP_KERNEL | __GFP_ZERO); - if (!p) - return -ENOMEM; - - q->hdev = hdev; - q->kernel_address = (u64) (uintptr_t) p; - q->hw_queue_id = hw_queue_id; - q->ci = 0; - q->pi = 0; - - atomic_set(&q->free_slots_cnt, HL_CQ_LENGTH); - - return 0; -} - -/** - * hl_cq_fini - destroy completion queue - * - * @hdev: pointer to device structure - * @q: pointer to cq structure - * - * Free the completion queue memory - */ -void hl_cq_fini(struct hl_device *hdev, struct hl_cq *q) -{ - hdev->asic_funcs->asic_dma_free_coherent(hdev, HL_CQ_SIZE_IN_BYTES, - (void *) (uintptr_t) q->kernel_address, q->bus_address); -} - -void hl_cq_reset(struct hl_device *hdev, struct hl_cq *q) -{ - q->ci = 0; - q->pi = 0; - - atomic_set(&q->free_slots_cnt, HL_CQ_LENGTH); - - /* - * It's not enough to just reset the PI/CI because the H/W may have - * written valid completion entries before it was halted and therefore - * we need to clean the actual queues so we won't process old entries - * when the device is operational again - */ - - memset((void *) (uintptr_t) q->kernel_address, 0, HL_CQ_SIZE_IN_BYTES); -} - -/** - * hl_eq_init - main initialization function for an event queue object - * - * @hdev: pointer to device structure - * @q: pointer to eq structure - * - * Allocate dma-able memory for the event queue and initialize fields - * Returns 0 on success - */ -int hl_eq_init(struct hl_device *hdev, struct hl_eq *q) -{ - void *p; - - p = hdev->asic_funcs->cpu_accessible_dma_pool_alloc(hdev, - HL_EQ_SIZE_IN_BYTES, - &q->bus_address); - if (!p) - return -ENOMEM; - - q->hdev = hdev; - q->kernel_address = (u64) (uintptr_t) p; - q->ci = 0; - - return 0; -} - -/** - * hl_eq_fini - destroy event queue - * - * @hdev: pointer to device structure - * @q: pointer to eq structure - * - * Free the event queue memory - */ -void hl_eq_fini(struct hl_device *hdev, struct hl_eq *q) -{ - flush_workqueue(hdev->eq_wq); - - hdev->asic_funcs->cpu_accessible_dma_pool_free(hdev, - HL_EQ_SIZE_IN_BYTES, - (void *) (uintptr_t) q->kernel_address); -} - -void hl_eq_reset(struct hl_device *hdev, struct hl_eq *q) -{ - q->ci = 0; - - /* - * It's not enough to just reset the PI/CI because the H/W may have - * written valid completion entries before it was halted and therefore - * we need to clean the actual queues so we won't process old entries - * when the device is operational again - */ - - memset((void *) (uintptr_t) q->kernel_address, 0, HL_EQ_SIZE_IN_BYTES); -} diff --git a/drivers/misc/habanalabs/memory.c b/drivers/misc/habanalabs/memory.c deleted file mode 100644 index e4e1693e5c6c..000000000000 --- a/drivers/misc/habanalabs/memory.c +++ /dev/null @@ -1,1843 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -/* - * Copyright 2016-2019 HabanaLabs, Ltd. - * All Rights Reserved. - */ - -#include -#include "habanalabs.h" -#include "include/hw_ip/mmu/mmu_general.h" - -#include -#include -#include - -#define HL_MMU_DEBUG 0 - -/* - * The va ranges in context object contain a list with the available chunks of - * device virtual memory. - * There is one range for host allocations and one for DRAM allocations. - * - * On initialization each range contains one chunk of all of its available - * virtual range which is a half of the total device virtual range. - * - * On each mapping of physical pages, a suitable virtual range chunk (with a - * minimum size) is selected from the list. If the chunk size equals the - * requested size, the chunk is returned. Otherwise, the chunk is split into - * two chunks - one to return as result and a remainder to stay in the list. - * - * On each Unmapping of a virtual address, the relevant virtual chunk is - * returned to the list. The chunk is added to the list and if its edges match - * the edges of the adjacent chunks (means a contiguous chunk can be created), - * the chunks are merged. - * - * On finish, the list is checked to have only one chunk of all the relevant - * virtual range (which is a half of the device total virtual range). - * If not (means not all mappings were unmapped), a warning is printed. - */ - -/* - * alloc_device_memory - allocate device memory - * - * @ctx : current context - * @args : host parameters containing the requested size - * @ret_handle : result handle - * - * This function does the following: - * - Allocate the requested size rounded up to 2MB pages - * - Return unique handle - */ -static int alloc_device_memory(struct hl_ctx *ctx, struct hl_mem_in *args, - u32 *ret_handle) -{ - struct hl_device *hdev = ctx->hdev; - struct hl_vm *vm = &hdev->vm; - struct hl_vm_phys_pg_pack *phys_pg_pack; - u64 paddr = 0, total_size, num_pgs, i; - u32 num_curr_pgs, page_size, page_shift; - int handle, rc; - bool contiguous; - - num_curr_pgs = 0; - page_size = hdev->asic_prop.dram_page_size; - page_shift = __ffs(page_size); - num_pgs = (args->alloc.mem_size + (page_size - 1)) >> page_shift; - total_size = num_pgs << page_shift; - - contiguous = args->flags & HL_MEM_CONTIGUOUS; - - if (contiguous) { - paddr = (u64) gen_pool_alloc(vm->dram_pg_pool, total_size); - if (!paddr) { - dev_err(hdev->dev, - "failed to allocate %llu huge contiguous pages\n", - num_pgs); - return -ENOMEM; - } - } - - phys_pg_pack = kzalloc(sizeof(*phys_pg_pack), GFP_KERNEL); - if (!phys_pg_pack) { - rc = -ENOMEM; - goto pages_pack_err; - } - - phys_pg_pack->vm_type = VM_TYPE_PHYS_PACK; - phys_pg_pack->asid = ctx->asid; - phys_pg_pack->npages = num_pgs; - phys_pg_pack->page_size = page_size; - phys_pg_pack->total_size = total_size; - phys_pg_pack->flags = args->flags; - phys_pg_pack->contiguous = contiguous; - - phys_pg_pack->pages = kvmalloc_array(num_pgs, sizeof(u64), GFP_KERNEL); - if (!phys_pg_pack->pages) { - rc = -ENOMEM; - goto pages_arr_err; - } - - if (phys_pg_pack->contiguous) { - for (i = 0 ; i < num_pgs ; i++) - phys_pg_pack->pages[i] = paddr + i * page_size; - } else { - for (i = 0 ; i < num_pgs ; i++) { - phys_pg_pack->pages[i] = (u64) gen_pool_alloc( - vm->dram_pg_pool, - page_size); - if (!phys_pg_pack->pages[i]) { - dev_err(hdev->dev, - "Failed to allocate device memory (out of memory)\n"); - rc = -ENOMEM; - goto page_err; - } - - num_curr_pgs++; - } - } - - spin_lock(&vm->idr_lock); - handle = idr_alloc(&vm->phys_pg_pack_handles, phys_pg_pack, 1, 0, - GFP_ATOMIC); - spin_unlock(&vm->idr_lock); - - if (handle < 0) { - dev_err(hdev->dev, "Failed to get handle for page\n"); - rc = -EFAULT; - goto idr_err; - } - - for (i = 0 ; i < num_pgs ; i++) - kref_get(&vm->dram_pg_pool_refcount); - - phys_pg_pack->handle = handle; - - atomic64_add(phys_pg_pack->total_size, &ctx->dram_phys_mem); - atomic64_add(phys_pg_pack->total_size, &hdev->dram_used_mem); - - *ret_handle = handle; - - return 0; - -idr_err: -page_err: - if (!phys_pg_pack->contiguous) - for (i = 0 ; i < num_curr_pgs ; i++) - gen_pool_free(vm->dram_pg_pool, phys_pg_pack->pages[i], - page_size); - - kvfree(phys_pg_pack->pages); -pages_arr_err: - kfree(phys_pg_pack); -pages_pack_err: - if (contiguous) - gen_pool_free(vm->dram_pg_pool, paddr, total_size); - - return rc; -} - -/* - * dma_map_host_va - DMA mapping of the given host virtual address. - * @hdev: habanalabs device structure - * @addr: the host virtual address of the memory area - * @size: the size of the memory area - * @p_userptr: pointer to result userptr structure - * - * This function does the following: - * - Allocate userptr structure - * - Pin the given host memory using the userptr structure - * - Perform DMA mapping to have the DMA addresses of the pages - */ -static int dma_map_host_va(struct hl_device *hdev, u64 addr, u64 size, - struct hl_userptr **p_userptr) -{ - struct hl_userptr *userptr; - int rc; - - userptr = kzalloc(sizeof(*userptr), GFP_KERNEL); - if (!userptr) { - rc = -ENOMEM; - goto userptr_err; - } - - rc = hl_pin_host_memory(hdev, addr, size, userptr); - if (rc) { - dev_err(hdev->dev, "Failed to pin host memory\n"); - goto pin_err; - } - - rc = hdev->asic_funcs->asic_dma_map_sg(hdev, userptr->sgt->sgl, - userptr->sgt->nents, DMA_BIDIRECTIONAL); - if (rc) { - dev_err(hdev->dev, "failed to map sgt with DMA region\n"); - goto dma_map_err; - } - - userptr->dma_mapped = true; - userptr->dir = DMA_BIDIRECTIONAL; - userptr->vm_type = VM_TYPE_USERPTR; - - *p_userptr = userptr; - - return 0; - -dma_map_err: - hl_unpin_host_memory(hdev, userptr); -pin_err: - kfree(userptr); -userptr_err: - - return rc; -} - -/* - * dma_unmap_host_va - DMA unmapping of the given host virtual address. - * @hdev: habanalabs device structure - * @userptr: userptr to free - * - * This function does the following: - * - Unpins the physical pages - * - Frees the userptr structure - */ -static void dma_unmap_host_va(struct hl_device *hdev, - struct hl_userptr *userptr) -{ - hl_unpin_host_memory(hdev, userptr); - kfree(userptr); -} - -/* - * dram_pg_pool_do_release - free DRAM pages pool - * - * @ref : pointer to reference object - * - * This function does the following: - * - Frees the idr structure of physical pages handles - * - Frees the generic pool of DRAM physical pages - */ -static void dram_pg_pool_do_release(struct kref *ref) -{ - struct hl_vm *vm = container_of(ref, struct hl_vm, - dram_pg_pool_refcount); - - /* - * free the idr here as only here we know for sure that there are no - * allocated physical pages and hence there are no handles in use - */ - idr_destroy(&vm->phys_pg_pack_handles); - gen_pool_destroy(vm->dram_pg_pool); -} - -/* - * free_phys_pg_pack - free physical page pack - * @hdev: habanalabs device structure - * @phys_pg_pack: physical page pack to free - * - * This function does the following: - * - For DRAM memory only, iterate over the pack and free each physical block - * structure by returning it to the general pool - * - Free the hl_vm_phys_pg_pack structure - */ -static void free_phys_pg_pack(struct hl_device *hdev, - struct hl_vm_phys_pg_pack *phys_pg_pack) -{ - struct hl_vm *vm = &hdev->vm; - u64 i; - - if (!phys_pg_pack->created_from_userptr) { - if (phys_pg_pack->contiguous) { - gen_pool_free(vm->dram_pg_pool, phys_pg_pack->pages[0], - phys_pg_pack->total_size); - - for (i = 0; i < phys_pg_pack->npages ; i++) - kref_put(&vm->dram_pg_pool_refcount, - dram_pg_pool_do_release); - } else { - for (i = 0 ; i < phys_pg_pack->npages ; i++) { - gen_pool_free(vm->dram_pg_pool, - phys_pg_pack->pages[i], - phys_pg_pack->page_size); - kref_put(&vm->dram_pg_pool_refcount, - dram_pg_pool_do_release); - } - } - } - - kvfree(phys_pg_pack->pages); - kfree(phys_pg_pack); -} - -/* - * free_device_memory - free device memory - * - * @ctx : current context - * @handle : handle of the memory chunk to free - * - * This function does the following: - * - Free the device memory related to the given handle - */ -static int free_device_memory(struct hl_ctx *ctx, u32 handle) -{ - struct hl_device *hdev = ctx->hdev; - struct hl_vm *vm = &hdev->vm; - struct hl_vm_phys_pg_pack *phys_pg_pack; - - spin_lock(&vm->idr_lock); - phys_pg_pack = idr_find(&vm->phys_pg_pack_handles, handle); - if (phys_pg_pack) { - if (atomic_read(&phys_pg_pack->mapping_cnt) > 0) { - dev_err(hdev->dev, "handle %u is mapped, cannot free\n", - handle); - spin_unlock(&vm->idr_lock); - return -EINVAL; - } - - /* - * must remove from idr before the freeing of the physical - * pages as the refcount of the pool is also the trigger of the - * idr destroy - */ - idr_remove(&vm->phys_pg_pack_handles, handle); - spin_unlock(&vm->idr_lock); - - atomic64_sub(phys_pg_pack->total_size, &ctx->dram_phys_mem); - atomic64_sub(phys_pg_pack->total_size, &hdev->dram_used_mem); - - free_phys_pg_pack(hdev, phys_pg_pack); - } else { - spin_unlock(&vm->idr_lock); - dev_err(hdev->dev, - "free device memory failed, no match for handle %u\n", - handle); - return -EINVAL; - } - - return 0; -} - -/* - * clear_va_list_locked - free virtual addresses list - * - * @hdev : habanalabs device structure - * @va_list : list of virtual addresses to free - * - * This function does the following: - * - Iterate over the list and free each virtual addresses block - * - * This function should be called only when va_list lock is taken - */ -static void clear_va_list_locked(struct hl_device *hdev, - struct list_head *va_list) -{ - struct hl_vm_va_block *va_block, *tmp; - - list_for_each_entry_safe(va_block, tmp, va_list, node) { - list_del(&va_block->node); - kfree(va_block); - } -} - -/* - * print_va_list_locked - print virtual addresses list - * - * @hdev : habanalabs device structure - * @va_list : list of virtual addresses to print - * - * This function does the following: - * - Iterate over the list and print each virtual addresses block - * - * This function should be called only when va_list lock is taken - */ -static void print_va_list_locked(struct hl_device *hdev, - struct list_head *va_list) -{ -#if HL_MMU_DEBUG - struct hl_vm_va_block *va_block; - - dev_dbg(hdev->dev, "print va list:\n"); - - list_for_each_entry(va_block, va_list, node) - dev_dbg(hdev->dev, - "va block, start: 0x%llx, end: 0x%llx, size: %llu\n", - va_block->start, va_block->end, va_block->size); -#endif -} - -/* - * merge_va_blocks_locked - merge a virtual block if possible - * - * @hdev : pointer to the habanalabs device structure - * @va_list : pointer to the virtual addresses block list - * @va_block : virtual block to merge with adjacent blocks - * - * This function does the following: - * - Merge the given blocks with the adjacent blocks if their virtual ranges - * create a contiguous virtual range - * - * This Function should be called only when va_list lock is taken - */ -static void merge_va_blocks_locked(struct hl_device *hdev, - struct list_head *va_list, struct hl_vm_va_block *va_block) -{ - struct hl_vm_va_block *prev, *next; - - prev = list_prev_entry(va_block, node); - if (&prev->node != va_list && prev->end + 1 == va_block->start) { - prev->end = va_block->end; - prev->size = prev->end - prev->start; - list_del(&va_block->node); - kfree(va_block); - va_block = prev; - } - - next = list_next_entry(va_block, node); - if (&next->node != va_list && va_block->end + 1 == next->start) { - next->start = va_block->start; - next->size = next->end - next->start; - list_del(&va_block->node); - kfree(va_block); - } -} - -/* - * add_va_block_locked - add a virtual block to the virtual addresses list - * - * @hdev : pointer to the habanalabs device structure - * @va_list : pointer to the virtual addresses block list - * @start : start virtual address - * @end : end virtual address - * - * This function does the following: - * - Add the given block to the virtual blocks list and merge with other - * blocks if a contiguous virtual block can be created - * - * This Function should be called only when va_list lock is taken - */ -static int add_va_block_locked(struct hl_device *hdev, - struct list_head *va_list, u64 start, u64 end) -{ - struct hl_vm_va_block *va_block, *res = NULL; - u64 size = end - start; - - print_va_list_locked(hdev, va_list); - - list_for_each_entry(va_block, va_list, node) { - /* TODO: remove upon matureness */ - if (hl_mem_area_crosses_range(start, size, va_block->start, - va_block->end)) { - dev_err(hdev->dev, - "block crossing ranges at start 0x%llx, end 0x%llx\n", - va_block->start, va_block->end); - return -EINVAL; - } - - if (va_block->end < start) - res = va_block; - } - - va_block = kmalloc(sizeof(*va_block), GFP_KERNEL); - if (!va_block) - return -ENOMEM; - - va_block->start = start; - va_block->end = end; - va_block->size = size; - - if (!res) - list_add(&va_block->node, va_list); - else - list_add(&va_block->node, &res->node); - - merge_va_blocks_locked(hdev, va_list, va_block); - - print_va_list_locked(hdev, va_list); - - return 0; -} - -/* - * add_va_block - wrapper for add_va_block_locked - * - * @hdev : pointer to the habanalabs device structure - * @va_list : pointer to the virtual addresses block list - * @start : start virtual address - * @end : end virtual address - * - * This function does the following: - * - Takes the list lock and calls add_va_block_locked - */ -static inline int add_va_block(struct hl_device *hdev, - struct hl_va_range *va_range, u64 start, u64 end) -{ - int rc; - - mutex_lock(&va_range->lock); - rc = add_va_block_locked(hdev, &va_range->list, start, end); - mutex_unlock(&va_range->lock); - - return rc; -} - -/* - * get_va_block - get a virtual block with the requested size - * - * @hdev : pointer to the habanalabs device structure - * @va_range : pointer to the virtual addresses range - * @size : requested block size - * @hint_addr : hint for request address by the user - * @is_userptr : is host or DRAM memory - * - * This function does the following: - * - Iterate on the virtual block list to find a suitable virtual block for the - * requested size - * - Reserve the requested block and update the list - * - Return the start address of the virtual block - */ -static u64 get_va_block(struct hl_device *hdev, - struct hl_va_range *va_range, u64 size, u64 hint_addr, - bool is_userptr) -{ - struct hl_vm_va_block *va_block, *new_va_block = NULL; - u64 valid_start, valid_size, prev_start, prev_end, page_mask, - res_valid_start = 0, res_valid_size = 0; - u32 page_size; - bool add_prev = false; - - if (is_userptr) - /* - * We cannot know if the user allocated memory with huge pages - * or not, hence we continue with the biggest possible - * granularity. - */ - page_size = hdev->asic_prop.pmmu_huge.page_size; - else - page_size = hdev->asic_prop.dmmu.page_size; - - page_mask = ~((u64)page_size - 1); - - mutex_lock(&va_range->lock); - - print_va_list_locked(hdev, &va_range->list); - - list_for_each_entry(va_block, &va_range->list, node) { - /* calc the first possible aligned addr */ - valid_start = va_block->start; - - if (valid_start & (page_size - 1)) { - valid_start &= page_mask; - valid_start += page_size; - if (valid_start > va_block->end) - continue; - } - - valid_size = va_block->end - valid_start; - - if (valid_size >= size && - (!new_va_block || valid_size < res_valid_size)) { - new_va_block = va_block; - res_valid_start = valid_start; - res_valid_size = valid_size; - } - - if (hint_addr && hint_addr >= valid_start && - ((hint_addr + size) <= va_block->end)) { - new_va_block = va_block; - res_valid_start = hint_addr; - res_valid_size = valid_size; - break; - } - } - - if (!new_va_block) { - dev_err(hdev->dev, "no available va block for size %llu\n", - size); - goto out; - } - - if (res_valid_start > new_va_block->start) { - prev_start = new_va_block->start; - prev_end = res_valid_start - 1; - - new_va_block->start = res_valid_start; - new_va_block->size = res_valid_size; - - add_prev = true; - } - - if (new_va_block->size > size) { - new_va_block->start += size; - new_va_block->size = new_va_block->end - new_va_block->start; - } else { - list_del(&new_va_block->node); - kfree(new_va_block); - } - - if (add_prev) - add_va_block_locked(hdev, &va_range->list, prev_start, - prev_end); - - print_va_list_locked(hdev, &va_range->list); -out: - mutex_unlock(&va_range->lock); - - return res_valid_start; -} - -/* - * get_sg_info - get number of pages and the DMA address from SG list - * - * @sg : the SG list - * @dma_addr : pointer to DMA address to return - * - * Calculate the number of consecutive pages described by the SG list. Take the - * offset of the address in the first page, add to it the length and round it up - * to the number of needed pages. - */ -static u32 get_sg_info(struct scatterlist *sg, dma_addr_t *dma_addr) -{ - *dma_addr = sg_dma_address(sg); - - return ((((*dma_addr) & (PAGE_SIZE - 1)) + sg_dma_len(sg)) + - (PAGE_SIZE - 1)) >> PAGE_SHIFT; -} - -/* - * init_phys_pg_pack_from_userptr - initialize physical page pack from host - * memory - * @ctx: current context - * @userptr: userptr to initialize from - * @pphys_pg_pack: result pointer - * - * This function does the following: - * - Pin the physical pages related to the given virtual block - * - Create a physical page pack from the physical pages related to the given - * virtual block - */ -static int init_phys_pg_pack_from_userptr(struct hl_ctx *ctx, - struct hl_userptr *userptr, - struct hl_vm_phys_pg_pack **pphys_pg_pack) -{ - struct hl_vm_phys_pg_pack *phys_pg_pack; - struct scatterlist *sg; - dma_addr_t dma_addr; - u64 page_mask, total_npages; - u32 npages, page_size = PAGE_SIZE, - huge_page_size = ctx->hdev->asic_prop.pmmu_huge.page_size; - bool first = true, is_huge_page_opt = true; - int rc, i, j; - u32 pgs_in_huge_page = huge_page_size >> __ffs(page_size); - - phys_pg_pack = kzalloc(sizeof(*phys_pg_pack), GFP_KERNEL); - if (!phys_pg_pack) - return -ENOMEM; - - phys_pg_pack->vm_type = userptr->vm_type; - phys_pg_pack->created_from_userptr = true; - phys_pg_pack->asid = ctx->asid; - atomic_set(&phys_pg_pack->mapping_cnt, 1); - - /* Only if all dma_addrs are aligned to 2MB and their - * sizes is at least 2MB, we can use huge page mapping. - * We limit the 2MB optimization to this condition, - * since later on we acquire the related VA range as one - * consecutive block. - */ - total_npages = 0; - for_each_sg(userptr->sgt->sgl, sg, userptr->sgt->nents, i) { - npages = get_sg_info(sg, &dma_addr); - - total_npages += npages; - - if ((npages % pgs_in_huge_page) || - (dma_addr & (huge_page_size - 1))) - is_huge_page_opt = false; - } - - if (is_huge_page_opt) { - page_size = huge_page_size; - do_div(total_npages, pgs_in_huge_page); - } - - page_mask = ~(((u64) page_size) - 1); - - phys_pg_pack->pages = kvmalloc_array(total_npages, sizeof(u64), - GFP_KERNEL); - if (!phys_pg_pack->pages) { - rc = -ENOMEM; - goto page_pack_arr_mem_err; - } - - phys_pg_pack->npages = total_npages; - phys_pg_pack->page_size = page_size; - phys_pg_pack->total_size = total_npages * page_size; - - j = 0; - for_each_sg(userptr->sgt->sgl, sg, userptr->sgt->nents, i) { - npages = get_sg_info(sg, &dma_addr); - - /* align down to physical page size and save the offset */ - if (first) { - first = false; - phys_pg_pack->offset = dma_addr & (page_size - 1); - dma_addr &= page_mask; - } - - while (npages) { - phys_pg_pack->pages[j++] = dma_addr; - dma_addr += page_size; - - if (is_huge_page_opt) - npages -= pgs_in_huge_page; - else - npages--; - } - } - - *pphys_pg_pack = phys_pg_pack; - - return 0; - -page_pack_arr_mem_err: - kfree(phys_pg_pack); - - return rc; -} - -/* - * map_phys_pg_pack - maps the physical page pack. - * @ctx: current context - * @vaddr: start address of the virtual area to map from - * @phys_pg_pack: the pack of physical pages to map to - * - * This function does the following: - * - Maps each chunk of virtual memory to matching physical chunk - * - Stores number of successful mappings in the given argument - * - Returns 0 on success, error code otherwise - */ -static int map_phys_pg_pack(struct hl_ctx *ctx, u64 vaddr, - struct hl_vm_phys_pg_pack *phys_pg_pack) -{ - struct hl_device *hdev = ctx->hdev; - u64 next_vaddr = vaddr, paddr, mapped_pg_cnt = 0, i; - u32 page_size = phys_pg_pack->page_size; - int rc = 0; - - for (i = 0 ; i < phys_pg_pack->npages ; i++) { - paddr = phys_pg_pack->pages[i]; - - rc = hl_mmu_map(ctx, next_vaddr, paddr, page_size, - (i + 1) == phys_pg_pack->npages); - if (rc) { - dev_err(hdev->dev, - "map failed for handle %u, npages: %llu, mapped: %llu", - phys_pg_pack->handle, phys_pg_pack->npages, - mapped_pg_cnt); - goto err; - } - - mapped_pg_cnt++; - next_vaddr += page_size; - } - - return 0; - -err: - next_vaddr = vaddr; - for (i = 0 ; i < mapped_pg_cnt ; i++) { - if (hl_mmu_unmap(ctx, next_vaddr, page_size, - (i + 1) == mapped_pg_cnt)) - dev_warn_ratelimited(hdev->dev, - "failed to unmap handle %u, va: 0x%llx, pa: 0x%llx, page size: %u\n", - phys_pg_pack->handle, next_vaddr, - phys_pg_pack->pages[i], page_size); - - next_vaddr += page_size; - } - - return rc; -} - -/* - * unmap_phys_pg_pack - unmaps the physical page pack - * @ctx: current context - * @vaddr: start address of the virtual area to unmap - * @phys_pg_pack: the pack of physical pages to unmap - */ -static void unmap_phys_pg_pack(struct hl_ctx *ctx, u64 vaddr, - struct hl_vm_phys_pg_pack *phys_pg_pack) -{ - struct hl_device *hdev = ctx->hdev; - u64 next_vaddr, i; - u32 page_size; - - page_size = phys_pg_pack->page_size; - next_vaddr = vaddr; - - for (i = 0 ; i < phys_pg_pack->npages ; i++, next_vaddr += page_size) { - if (hl_mmu_unmap(ctx, next_vaddr, page_size, - (i + 1) == phys_pg_pack->npages)) - dev_warn_ratelimited(hdev->dev, - "unmap failed for vaddr: 0x%llx\n", next_vaddr); - - /* - * unmapping on Palladium can be really long, so avoid a CPU - * soft lockup bug by sleeping a little between unmapping pages - */ - if (hdev->pldm) - usleep_range(500, 1000); - } -} - -static int get_paddr_from_handle(struct hl_ctx *ctx, struct hl_mem_in *args, - u64 *paddr) -{ - struct hl_device *hdev = ctx->hdev; - struct hl_vm *vm = &hdev->vm; - struct hl_vm_phys_pg_pack *phys_pg_pack; - u32 handle; - - handle = lower_32_bits(args->map_device.handle); - spin_lock(&vm->idr_lock); - phys_pg_pack = idr_find(&vm->phys_pg_pack_handles, handle); - if (!phys_pg_pack) { - spin_unlock(&vm->idr_lock); - dev_err(hdev->dev, "no match for handle %u\n", handle); - return -EINVAL; - } - - *paddr = phys_pg_pack->pages[0]; - - spin_unlock(&vm->idr_lock); - - return 0; -} - -/* - * map_device_va - map the given memory - * - * @ctx : current context - * @args : host parameters with handle/host virtual address - * @device_addr : pointer to result device virtual address - * - * This function does the following: - * - If given a physical device memory handle, map to a device virtual block - * and return the start address of this block - * - If given a host virtual address and size, find the related physical pages, - * map a device virtual block to this pages and return the start address of - * this block - */ -static int map_device_va(struct hl_ctx *ctx, struct hl_mem_in *args, - u64 *device_addr) -{ - struct hl_device *hdev = ctx->hdev; - struct hl_vm *vm = &hdev->vm; - struct hl_vm_phys_pg_pack *phys_pg_pack; - struct hl_userptr *userptr = NULL; - struct hl_vm_hash_node *hnode; - struct hl_va_range *va_range; - enum vm_type_t *vm_type; - u64 ret_vaddr, hint_addr; - u32 handle = 0; - int rc; - bool is_userptr = args->flags & HL_MEM_USERPTR; - - /* Assume failure */ - *device_addr = 0; - - if (is_userptr) { - u64 addr = args->map_host.host_virt_addr, - size = args->map_host.mem_size; - - rc = dma_map_host_va(hdev, addr, size, &userptr); - if (rc) { - dev_err(hdev->dev, "failed to get userptr from va\n"); - return rc; - } - - rc = init_phys_pg_pack_from_userptr(ctx, userptr, - &phys_pg_pack); - if (rc) { - dev_err(hdev->dev, - "unable to init page pack for vaddr 0x%llx\n", - addr); - goto init_page_pack_err; - } - - vm_type = (enum vm_type_t *) userptr; - hint_addr = args->map_host.hint_addr; - handle = phys_pg_pack->handle; - } else { - handle = lower_32_bits(args->map_device.handle); - - spin_lock(&vm->idr_lock); - phys_pg_pack = idr_find(&vm->phys_pg_pack_handles, handle); - if (!phys_pg_pack) { - spin_unlock(&vm->idr_lock); - dev_err(hdev->dev, - "no match for handle %u\n", handle); - return -EINVAL; - } - - /* increment now to avoid freeing device memory while mapping */ - atomic_inc(&phys_pg_pack->mapping_cnt); - - spin_unlock(&vm->idr_lock); - - vm_type = (enum vm_type_t *) phys_pg_pack; - - hint_addr = args->map_device.hint_addr; - } - - /* - * relevant for mapping device physical memory only, as host memory is - * implicitly shared - */ - if (!is_userptr && !(phys_pg_pack->flags & HL_MEM_SHARED) && - phys_pg_pack->asid != ctx->asid) { - dev_err(hdev->dev, - "Failed to map memory, handle %u is not shared\n", - handle); - rc = -EPERM; - goto shared_err; - } - - hnode = kzalloc(sizeof(*hnode), GFP_KERNEL); - if (!hnode) { - rc = -ENOMEM; - goto hnode_err; - } - - if (is_userptr) - if (phys_pg_pack->page_size == hdev->asic_prop.pmmu.page_size) - va_range = ctx->host_va_range; - else - va_range = ctx->host_huge_va_range; - else - va_range = ctx->dram_va_range; - - ret_vaddr = get_va_block(hdev, va_range, phys_pg_pack->total_size, - hint_addr, is_userptr); - if (!ret_vaddr) { - dev_err(hdev->dev, "no available va block for handle %u\n", - handle); - rc = -ENOMEM; - goto va_block_err; - } - - mutex_lock(&ctx->mmu_lock); - - rc = map_phys_pg_pack(ctx, ret_vaddr, phys_pg_pack); - if (rc) { - mutex_unlock(&ctx->mmu_lock); - dev_err(hdev->dev, "mapping page pack failed for handle %u\n", - handle); - goto map_err; - } - - rc = hdev->asic_funcs->mmu_invalidate_cache(hdev, false, *vm_type); - - mutex_unlock(&ctx->mmu_lock); - - if (rc) { - dev_err(hdev->dev, - "mapping handle %u failed due to MMU cache invalidation\n", - handle); - goto map_err; - } - - ret_vaddr += phys_pg_pack->offset; - - hnode->ptr = vm_type; - hnode->vaddr = ret_vaddr; - - mutex_lock(&ctx->mem_hash_lock); - hash_add(ctx->mem_hash, &hnode->node, ret_vaddr); - mutex_unlock(&ctx->mem_hash_lock); - - *device_addr = ret_vaddr; - - if (is_userptr) - free_phys_pg_pack(hdev, phys_pg_pack); - - return 0; - -map_err: - if (add_va_block(hdev, va_range, ret_vaddr, - ret_vaddr + phys_pg_pack->total_size - 1)) - dev_warn(hdev->dev, - "release va block failed for handle 0x%x, vaddr: 0x%llx\n", - handle, ret_vaddr); - -va_block_err: - kfree(hnode); -hnode_err: -shared_err: - atomic_dec(&phys_pg_pack->mapping_cnt); - if (is_userptr) - free_phys_pg_pack(hdev, phys_pg_pack); -init_page_pack_err: - if (is_userptr) - dma_unmap_host_va(hdev, userptr); - - return rc; -} - -/* - * unmap_device_va - unmap the given device virtual address - * - * @ctx : current context - * @vaddr : device virtual address to unmap - * @ctx_free : true if in context free flow, false otherwise. - * - * This function does the following: - * - Unmap the physical pages related to the given virtual address - * - return the device virtual block to the virtual block list - */ -static int unmap_device_va(struct hl_ctx *ctx, u64 vaddr, bool ctx_free) -{ - struct hl_device *hdev = ctx->hdev; - struct hl_vm_phys_pg_pack *phys_pg_pack = NULL; - struct hl_vm_hash_node *hnode = NULL; - struct hl_userptr *userptr = NULL; - struct hl_va_range *va_range; - enum vm_type_t *vm_type; - bool is_userptr; - int rc = 0; - - /* protect from double entrance */ - mutex_lock(&ctx->mem_hash_lock); - hash_for_each_possible(ctx->mem_hash, hnode, node, (unsigned long)vaddr) - if (vaddr == hnode->vaddr) - break; - - if (!hnode) { - mutex_unlock(&ctx->mem_hash_lock); - dev_err(hdev->dev, - "unmap failed, no mem hnode for vaddr 0x%llx\n", - vaddr); - return -EINVAL; - } - - hash_del(&hnode->node); - mutex_unlock(&ctx->mem_hash_lock); - - vm_type = hnode->ptr; - - if (*vm_type == VM_TYPE_USERPTR) { - is_userptr = true; - userptr = hnode->ptr; - rc = init_phys_pg_pack_from_userptr(ctx, userptr, - &phys_pg_pack); - if (rc) { - dev_err(hdev->dev, - "unable to init page pack for vaddr 0x%llx\n", - vaddr); - goto vm_type_err; - } - - if (phys_pg_pack->page_size == - hdev->asic_prop.pmmu.page_size) - va_range = ctx->host_va_range; - else - va_range = ctx->host_huge_va_range; - } else if (*vm_type == VM_TYPE_PHYS_PACK) { - is_userptr = false; - va_range = ctx->dram_va_range; - phys_pg_pack = hnode->ptr; - } else { - dev_warn(hdev->dev, - "unmap failed, unknown vm desc for vaddr 0x%llx\n", - vaddr); - rc = -EFAULT; - goto vm_type_err; - } - - if (atomic_read(&phys_pg_pack->mapping_cnt) == 0) { - dev_err(hdev->dev, "vaddr 0x%llx is not mapped\n", vaddr); - rc = -EINVAL; - goto mapping_cnt_err; - } - - vaddr &= ~(((u64) phys_pg_pack->page_size) - 1); - - mutex_lock(&ctx->mmu_lock); - - unmap_phys_pg_pack(ctx, vaddr, phys_pg_pack); - - /* - * During context free this function is called in a loop to clean all - * the context mappings. Hence the cache invalidation can be called once - * at the loop end rather than for each iteration - */ - if (!ctx_free) - rc = hdev->asic_funcs->mmu_invalidate_cache(hdev, true, - *vm_type); - - mutex_unlock(&ctx->mmu_lock); - - /* - * If the context is closing we don't need to check for the MMU cache - * invalidation return code and update the VA free list as in this flow - * we invalidate the MMU cache outside of this unmap function and the VA - * free list will be freed anyway. - */ - if (!ctx_free) { - int tmp_rc; - - if (rc) - dev_err(hdev->dev, - "unmapping vaddr 0x%llx failed due to MMU cache invalidation\n", - vaddr); - - tmp_rc = add_va_block(hdev, va_range, vaddr, - vaddr + phys_pg_pack->total_size - 1); - if (tmp_rc) { - dev_warn(hdev->dev, - "add va block failed for vaddr: 0x%llx\n", - vaddr); - if (!rc) - rc = tmp_rc; - } - } - - atomic_dec(&phys_pg_pack->mapping_cnt); - kfree(hnode); - - if (is_userptr) { - free_phys_pg_pack(hdev, phys_pg_pack); - dma_unmap_host_va(hdev, userptr); - } - - return rc; - -mapping_cnt_err: - if (is_userptr) - free_phys_pg_pack(hdev, phys_pg_pack); -vm_type_err: - mutex_lock(&ctx->mem_hash_lock); - hash_add(ctx->mem_hash, &hnode->node, vaddr); - mutex_unlock(&ctx->mem_hash_lock); - - return rc; -} - -static int mem_ioctl_no_mmu(struct hl_fpriv *hpriv, union hl_mem_args *args) -{ - struct hl_device *hdev = hpriv->hdev; - struct hl_ctx *ctx = hpriv->ctx; - u64 device_addr = 0; - u32 handle = 0; - int rc; - - switch (args->in.op) { - case HL_MEM_OP_ALLOC: - if (args->in.alloc.mem_size == 0) { - dev_err(hdev->dev, - "alloc size must be larger than 0\n"); - rc = -EINVAL; - goto out; - } - - /* Force contiguous as there are no real MMU - * translations to overcome physical memory gaps - */ - args->in.flags |= HL_MEM_CONTIGUOUS; - rc = alloc_device_memory(ctx, &args->in, &handle); - - memset(args, 0, sizeof(*args)); - args->out.handle = (__u64) handle; - break; - - case HL_MEM_OP_FREE: - rc = free_device_memory(ctx, args->in.free.handle); - break; - - case HL_MEM_OP_MAP: - if (args->in.flags & HL_MEM_USERPTR) { - device_addr = args->in.map_host.host_virt_addr; - rc = 0; - } else { - rc = get_paddr_from_handle(ctx, &args->in, - &device_addr); - } - - memset(args, 0, sizeof(*args)); - args->out.device_virt_addr = device_addr; - break; - - case HL_MEM_OP_UNMAP: - rc = 0; - break; - - default: - dev_err(hdev->dev, "Unknown opcode for memory IOCTL\n"); - rc = -ENOTTY; - break; - } - -out: - return rc; -} - -int hl_mem_ioctl(struct hl_fpriv *hpriv, void *data) -{ - union hl_mem_args *args = data; - struct hl_device *hdev = hpriv->hdev; - struct hl_ctx *ctx = hpriv->ctx; - u64 device_addr = 0; - u32 handle = 0; - int rc; - - if (hl_device_disabled_or_in_reset(hdev)) { - dev_warn_ratelimited(hdev->dev, - "Device is %s. Can't execute MEMORY IOCTL\n", - atomic_read(&hdev->in_reset) ? "in_reset" : "disabled"); - return -EBUSY; - } - - if (!hdev->mmu_enable) - return mem_ioctl_no_mmu(hpriv, args); - - switch (args->in.op) { - case HL_MEM_OP_ALLOC: - if (!hdev->dram_supports_virtual_memory) { - dev_err(hdev->dev, "DRAM alloc is not supported\n"); - rc = -EINVAL; - goto out; - } - - if (args->in.alloc.mem_size == 0) { - dev_err(hdev->dev, - "alloc size must be larger than 0\n"); - rc = -EINVAL; - goto out; - } - rc = alloc_device_memory(ctx, &args->in, &handle); - - memset(args, 0, sizeof(*args)); - args->out.handle = (__u64) handle; - break; - - case HL_MEM_OP_FREE: - rc = free_device_memory(ctx, args->in.free.handle); - break; - - case HL_MEM_OP_MAP: - rc = map_device_va(ctx, &args->in, &device_addr); - - memset(args, 0, sizeof(*args)); - args->out.device_virt_addr = device_addr; - break; - - case HL_MEM_OP_UNMAP: - rc = unmap_device_va(ctx, args->in.unmap.device_virt_addr, - false); - break; - - default: - dev_err(hdev->dev, "Unknown opcode for memory IOCTL\n"); - rc = -ENOTTY; - break; - } - -out: - return rc; -} - -static int get_user_memory(struct hl_device *hdev, u64 addr, u64 size, - u32 npages, u64 start, u32 offset, - struct hl_userptr *userptr) -{ - int rc; - - if (!access_ok((void __user *) (uintptr_t) addr, size)) { - dev_err(hdev->dev, "user pointer is invalid - 0x%llx\n", addr); - return -EFAULT; - } - - userptr->vec = frame_vector_create(npages); - if (!userptr->vec) { - dev_err(hdev->dev, "Failed to create frame vector\n"); - return -ENOMEM; - } - - rc = get_vaddr_frames(start, npages, FOLL_FORCE | FOLL_WRITE, - userptr->vec); - - if (rc != npages) { - dev_err(hdev->dev, - "Failed to map host memory, user ptr probably wrong\n"); - if (rc < 0) - goto destroy_framevec; - rc = -EFAULT; - goto put_framevec; - } - - if (frame_vector_to_pages(userptr->vec) < 0) { - dev_err(hdev->dev, - "Failed to translate frame vector to pages\n"); - rc = -EFAULT; - goto put_framevec; - } - - rc = sg_alloc_table_from_pages(userptr->sgt, - frame_vector_pages(userptr->vec), - npages, offset, size, GFP_ATOMIC); - if (rc < 0) { - dev_err(hdev->dev, "failed to create SG table from pages\n"); - goto put_framevec; - } - - return 0; - -put_framevec: - put_vaddr_frames(userptr->vec); -destroy_framevec: - frame_vector_destroy(userptr->vec); - return rc; -} - -/* - * hl_pin_host_memory - pins a chunk of host memory. - * @hdev: pointer to the habanalabs device structure - * @addr: the host virtual address of the memory area - * @size: the size of the memory area - * @userptr: pointer to hl_userptr structure - * - * This function does the following: - * - Pins the physical pages - * - Create an SG list from those pages - */ -int hl_pin_host_memory(struct hl_device *hdev, u64 addr, u64 size, - struct hl_userptr *userptr) -{ - u64 start, end; - u32 npages, offset; - int rc; - - if (!size) { - dev_err(hdev->dev, "size to pin is invalid - %llu\n", size); - return -EINVAL; - } - - /* - * If the combination of the address and size requested for this memory - * region causes an integer overflow, return error. - */ - if (((addr + size) < addr) || - PAGE_ALIGN(addr + size) < (addr + size)) { - dev_err(hdev->dev, - "user pointer 0x%llx + %llu causes integer overflow\n", - addr, size); - return -EINVAL; - } - - /* - * This function can be called also from data path, hence use atomic - * always as it is not a big allocation. - */ - userptr->sgt = kzalloc(sizeof(*userptr->sgt), GFP_ATOMIC); - if (!userptr->sgt) - return -ENOMEM; - - start = addr & PAGE_MASK; - offset = addr & ~PAGE_MASK; - end = PAGE_ALIGN(addr + size); - npages = (end - start) >> PAGE_SHIFT; - - userptr->size = size; - userptr->addr = addr; - userptr->dma_mapped = false; - INIT_LIST_HEAD(&userptr->job_node); - - rc = get_user_memory(hdev, addr, size, npages, start, offset, - userptr); - if (rc) { - dev_err(hdev->dev, - "failed to get user memory for address 0x%llx\n", - addr); - goto free_sgt; - } - - hl_debugfs_add_userptr(hdev, userptr); - - return 0; - -free_sgt: - kfree(userptr->sgt); - return rc; -} - -/* - * hl_unpin_host_memory - unpins a chunk of host memory. - * @hdev: pointer to the habanalabs device structure - * @userptr: pointer to hl_userptr structure - * - * This function does the following: - * - Unpins the physical pages related to the host memory - * - Free the SG list - */ -void hl_unpin_host_memory(struct hl_device *hdev, struct hl_userptr *userptr) -{ - struct page **pages; - - hl_debugfs_remove_userptr(hdev, userptr); - - if (userptr->dma_mapped) - hdev->asic_funcs->hl_dma_unmap_sg(hdev, userptr->sgt->sgl, - userptr->sgt->nents, - userptr->dir); - - pages = frame_vector_pages(userptr->vec); - if (!IS_ERR(pages)) { - int i; - - for (i = 0; i < frame_vector_count(userptr->vec); i++) - set_page_dirty_lock(pages[i]); - } - put_vaddr_frames(userptr->vec); - frame_vector_destroy(userptr->vec); - - list_del(&userptr->job_node); - - sg_free_table(userptr->sgt); - kfree(userptr->sgt); -} - -/* - * hl_userptr_delete_list - clear userptr list - * - * @hdev : pointer to the habanalabs device structure - * @userptr_list : pointer to the list to clear - * - * This function does the following: - * - Iterates over the list and unpins the host memory and frees the userptr - * structure. - */ -void hl_userptr_delete_list(struct hl_device *hdev, - struct list_head *userptr_list) -{ - struct hl_userptr *userptr, *tmp; - - list_for_each_entry_safe(userptr, tmp, userptr_list, job_node) { - hl_unpin_host_memory(hdev, userptr); - kfree(userptr); - } - - INIT_LIST_HEAD(userptr_list); -} - -/* - * hl_userptr_is_pinned - returns whether the given userptr is pinned - * - * @hdev : pointer to the habanalabs device structure - * @userptr_list : pointer to the list to clear - * @userptr : pointer to userptr to check - * - * This function does the following: - * - Iterates over the list and checks if the given userptr is in it, means is - * pinned. If so, returns true, otherwise returns false. - */ -bool hl_userptr_is_pinned(struct hl_device *hdev, u64 addr, - u32 size, struct list_head *userptr_list, - struct hl_userptr **userptr) -{ - list_for_each_entry((*userptr), userptr_list, job_node) { - if ((addr == (*userptr)->addr) && (size == (*userptr)->size)) - return true; - } - - return false; -} - -/* - * va_range_init - initialize virtual addresses range - * @hdev: pointer to the habanalabs device structure - * @va_range: pointer to the range to initialize - * @start: range start address - * @end: range end address - * - * This function does the following: - * - Initializes the virtual addresses list of the given range with the given - * addresses. - */ -static int va_range_init(struct hl_device *hdev, struct hl_va_range *va_range, - u64 start, u64 end) -{ - int rc; - - INIT_LIST_HEAD(&va_range->list); - - /* PAGE_SIZE alignment */ - - if (start & (PAGE_SIZE - 1)) { - start &= PAGE_MASK; - start += PAGE_SIZE; - } - - if (end & (PAGE_SIZE - 1)) - end &= PAGE_MASK; - - if (start >= end) { - dev_err(hdev->dev, "too small vm range for va list\n"); - return -EFAULT; - } - - rc = add_va_block(hdev, va_range, start, end); - - if (rc) { - dev_err(hdev->dev, "Failed to init host va list\n"); - return rc; - } - - va_range->start_addr = start; - va_range->end_addr = end; - - return 0; -} - -/* - * va_range_fini() - clear a virtual addresses range - * @hdev: pointer to the habanalabs structure - * va_range: pointer to virtual addresses range - * - * This function does the following: - * - Frees the virtual addresses block list and its lock - */ -static void va_range_fini(struct hl_device *hdev, - struct hl_va_range *va_range) -{ - mutex_lock(&va_range->lock); - clear_va_list_locked(hdev, &va_range->list); - mutex_unlock(&va_range->lock); - - mutex_destroy(&va_range->lock); - kfree(va_range); -} - -/* - * vm_ctx_init_with_ranges() - initialize virtual memory for context - * @ctx: pointer to the habanalabs context structure - * @host_range_start: host virtual addresses range start. - * @host_range_end: host virtual addresses range end. - * @host_huge_range_start: host virtual addresses range start for memory - * allocated with huge pages. - * @host_huge_range_end: host virtual addresses range end for memory allocated - * with huge pages. - * @dram_range_start: dram virtual addresses range start. - * @dram_range_end: dram virtual addresses range end. - * - * This function initializes the following: - * - MMU for context - * - Virtual address to area descriptor hashtable - * - Virtual block list of available virtual memory - */ -static int vm_ctx_init_with_ranges(struct hl_ctx *ctx, - u64 host_range_start, - u64 host_range_end, - u64 host_huge_range_start, - u64 host_huge_range_end, - u64 dram_range_start, - u64 dram_range_end) -{ - struct hl_device *hdev = ctx->hdev; - int rc; - - ctx->host_va_range = kzalloc(sizeof(*ctx->host_va_range), GFP_KERNEL); - if (!ctx->host_va_range) - return -ENOMEM; - - ctx->host_huge_va_range = kzalloc(sizeof(*ctx->host_huge_va_range), - GFP_KERNEL); - if (!ctx->host_huge_va_range) { - rc = -ENOMEM; - goto host_huge_va_range_err; - } - - ctx->dram_va_range = kzalloc(sizeof(*ctx->dram_va_range), GFP_KERNEL); - if (!ctx->dram_va_range) { - rc = -ENOMEM; - goto dram_va_range_err; - } - - rc = hl_mmu_ctx_init(ctx); - if (rc) { - dev_err(hdev->dev, "failed to init context %d\n", ctx->asid); - goto mmu_ctx_err; - } - - mutex_init(&ctx->mem_hash_lock); - hash_init(ctx->mem_hash); - - mutex_init(&ctx->host_va_range->lock); - - rc = va_range_init(hdev, ctx->host_va_range, host_range_start, - host_range_end); - if (rc) { - dev_err(hdev->dev, "failed to init host vm range\n"); - goto host_page_range_err; - } - - if (hdev->pmmu_huge_range) { - mutex_init(&ctx->host_huge_va_range->lock); - - rc = va_range_init(hdev, ctx->host_huge_va_range, - host_huge_range_start, - host_huge_range_end); - if (rc) { - dev_err(hdev->dev, - "failed to init host huge vm range\n"); - goto host_hpage_range_err; - } - } else { - ctx->host_huge_va_range = ctx->host_va_range; - } - - mutex_init(&ctx->dram_va_range->lock); - - rc = va_range_init(hdev, ctx->dram_va_range, dram_range_start, - dram_range_end); - if (rc) { - dev_err(hdev->dev, "failed to init dram vm range\n"); - goto dram_vm_err; - } - - hl_debugfs_add_ctx_mem_hash(hdev, ctx); - - return 0; - -dram_vm_err: - mutex_destroy(&ctx->dram_va_range->lock); - - if (hdev->pmmu_huge_range) { - mutex_lock(&ctx->host_huge_va_range->lock); - clear_va_list_locked(hdev, &ctx->host_huge_va_range->list); - mutex_unlock(&ctx->host_huge_va_range->lock); - } -host_hpage_range_err: - if (hdev->pmmu_huge_range) - mutex_destroy(&ctx->host_huge_va_range->lock); - mutex_lock(&ctx->host_va_range->lock); - clear_va_list_locked(hdev, &ctx->host_va_range->list); - mutex_unlock(&ctx->host_va_range->lock); -host_page_range_err: - mutex_destroy(&ctx->host_va_range->lock); - mutex_destroy(&ctx->mem_hash_lock); - hl_mmu_ctx_fini(ctx); -mmu_ctx_err: - kfree(ctx->dram_va_range); -dram_va_range_err: - kfree(ctx->host_huge_va_range); -host_huge_va_range_err: - kfree(ctx->host_va_range); - - return rc; -} - -int hl_vm_ctx_init(struct hl_ctx *ctx) -{ - struct asic_fixed_properties *prop = &ctx->hdev->asic_prop; - u64 host_range_start, host_range_end, host_huge_range_start, - host_huge_range_end, dram_range_start, dram_range_end; - - atomic64_set(&ctx->dram_phys_mem, 0); - - /* - * - If MMU is enabled, init the ranges as usual. - * - If MMU is disabled, in case of host mapping, the returned address - * is the given one. - * In case of DRAM mapping, the returned address is the physical - * address of the memory related to the given handle. - */ - if (ctx->hdev->mmu_enable) { - dram_range_start = prop->dmmu.start_addr; - dram_range_end = prop->dmmu.end_addr; - host_range_start = prop->pmmu.start_addr; - host_range_end = prop->pmmu.end_addr; - host_huge_range_start = prop->pmmu_huge.start_addr; - host_huge_range_end = prop->pmmu_huge.end_addr; - } else { - dram_range_start = prop->dram_user_base_address; - dram_range_end = prop->dram_end_address; - host_range_start = prop->dram_user_base_address; - host_range_end = prop->dram_end_address; - host_huge_range_start = prop->dram_user_base_address; - host_huge_range_end = prop->dram_end_address; - } - - return vm_ctx_init_with_ranges(ctx, host_range_start, host_range_end, - host_huge_range_start, - host_huge_range_end, - dram_range_start, - dram_range_end); -} - -/* - * hl_vm_ctx_fini - virtual memory teardown of context - * - * @ctx : pointer to the habanalabs context structure - * - * This function perform teardown the following: - * - Virtual block list of available virtual memory - * - Virtual address to area descriptor hashtable - * - MMU for context - * - * In addition this function does the following: - * - Unmaps the existing hashtable nodes if the hashtable is not empty. The - * hashtable should be empty as no valid mappings should exist at this - * point. - * - Frees any existing physical page list from the idr which relates to the - * current context asid. - * - This function checks the virtual block list for correctness. At this point - * the list should contain one element which describes the whole virtual - * memory range of the context. Otherwise, a warning is printed. - */ -void hl_vm_ctx_fini(struct hl_ctx *ctx) -{ - struct hl_device *hdev = ctx->hdev; - struct hl_vm *vm = &hdev->vm; - struct hl_vm_phys_pg_pack *phys_pg_list; - struct hl_vm_hash_node *hnode; - struct hlist_node *tmp_node; - int i; - - hl_debugfs_remove_ctx_mem_hash(hdev, ctx); - - /* - * Clearly something went wrong on hard reset so no point in printing - * another side effect error - */ - if (!hdev->hard_reset_pending && !hash_empty(ctx->mem_hash)) - dev_notice(hdev->dev, - "user released device without removing its memory mappings\n"); - - hash_for_each_safe(ctx->mem_hash, i, tmp_node, hnode, node) { - dev_dbg(hdev->dev, - "hl_mem_hash_node of vaddr 0x%llx of asid %d is still alive\n", - hnode->vaddr, ctx->asid); - unmap_device_va(ctx, hnode->vaddr, true); - } - - /* invalidate the cache once after the unmapping loop */ - hdev->asic_funcs->mmu_invalidate_cache(hdev, true, VM_TYPE_USERPTR); - hdev->asic_funcs->mmu_invalidate_cache(hdev, true, VM_TYPE_PHYS_PACK); - - spin_lock(&vm->idr_lock); - idr_for_each_entry(&vm->phys_pg_pack_handles, phys_pg_list, i) - if (phys_pg_list->asid == ctx->asid) { - dev_dbg(hdev->dev, - "page list 0x%px of asid %d is still alive\n", - phys_pg_list, ctx->asid); - atomic64_sub(phys_pg_list->total_size, - &hdev->dram_used_mem); - free_phys_pg_pack(hdev, phys_pg_list); - idr_remove(&vm->phys_pg_pack_handles, i); - } - spin_unlock(&vm->idr_lock); - - va_range_fini(hdev, ctx->dram_va_range); - if (hdev->pmmu_huge_range) - va_range_fini(hdev, ctx->host_huge_va_range); - va_range_fini(hdev, ctx->host_va_range); - - mutex_destroy(&ctx->mem_hash_lock); - hl_mmu_ctx_fini(ctx); -} - -/* - * hl_vm_init - initialize virtual memory module - * - * @hdev : pointer to the habanalabs device structure - * - * This function initializes the following: - * - MMU module - * - DRAM physical pages pool of 2MB - * - Idr for device memory allocation handles - */ -int hl_vm_init(struct hl_device *hdev) -{ - struct asic_fixed_properties *prop = &hdev->asic_prop; - struct hl_vm *vm = &hdev->vm; - int rc; - - vm->dram_pg_pool = gen_pool_create(__ffs(prop->dram_page_size), -1); - if (!vm->dram_pg_pool) { - dev_err(hdev->dev, "Failed to create dram page pool\n"); - return -ENOMEM; - } - - kref_init(&vm->dram_pg_pool_refcount); - - rc = gen_pool_add(vm->dram_pg_pool, prop->dram_user_base_address, - prop->dram_end_address - prop->dram_user_base_address, - -1); - - if (rc) { - dev_err(hdev->dev, - "Failed to add memory to dram page pool %d\n", rc); - goto pool_add_err; - } - - spin_lock_init(&vm->idr_lock); - idr_init(&vm->phys_pg_pack_handles); - - atomic64_set(&hdev->dram_used_mem, 0); - - vm->init_done = true; - - return 0; - -pool_add_err: - gen_pool_destroy(vm->dram_pg_pool); - - return rc; -} - -/* - * hl_vm_fini - virtual memory module teardown - * - * @hdev : pointer to the habanalabs device structure - * - * This function perform teardown to the following: - * - Idr for device memory allocation handles - * - DRAM physical pages pool of 2MB - * - MMU module - */ -void hl_vm_fini(struct hl_device *hdev) -{ - struct hl_vm *vm = &hdev->vm; - - if (!vm->init_done) - return; - - /* - * At this point all the contexts should be freed and hence no DRAM - * memory should be in use. Hence the DRAM pool should be freed here. - */ - if (kref_put(&vm->dram_pg_pool_refcount, dram_pg_pool_do_release) != 1) - dev_warn(hdev->dev, "dram_pg_pool was not destroyed on %s\n", - __func__); - - vm->init_done = false; -} diff --git a/drivers/misc/habanalabs/mmu.c b/drivers/misc/habanalabs/mmu.c deleted file mode 100644 index 04303950e630..000000000000 --- a/drivers/misc/habanalabs/mmu.c +++ /dev/null @@ -1,1037 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -/* - * Copyright 2016-2019 HabanaLabs, Ltd. - * All Rights Reserved. - */ - -#include "habanalabs.h" -#include "include/hw_ip/mmu/mmu_general.h" - -#include -#include - -static inline u64 get_phys_addr(struct hl_ctx *ctx, u64 shadow_addr); - -static struct pgt_info *get_pgt_info(struct hl_ctx *ctx, u64 hop_addr) -{ - struct pgt_info *pgt_info = NULL; - - hash_for_each_possible(ctx->mmu_shadow_hash, pgt_info, node, - (unsigned long) hop_addr) - if (hop_addr == pgt_info->shadow_addr) - break; - - return pgt_info; -} - -static void _free_hop(struct hl_ctx *ctx, struct pgt_info *pgt_info) -{ - struct hl_device *hdev = ctx->hdev; - - gen_pool_free(hdev->mmu_pgt_pool, pgt_info->phys_addr, - hdev->asic_prop.mmu_hop_table_size); - hash_del(&pgt_info->node); - kfree((u64 *) (uintptr_t) pgt_info->shadow_addr); - kfree(pgt_info); -} - -static void free_hop(struct hl_ctx *ctx, u64 hop_addr) -{ - struct pgt_info *pgt_info = get_pgt_info(ctx, hop_addr); - - _free_hop(ctx, pgt_info); -} - -static u64 alloc_hop(struct hl_ctx *ctx) -{ - struct hl_device *hdev = ctx->hdev; - struct asic_fixed_properties *prop = &hdev->asic_prop; - struct pgt_info *pgt_info; - u64 phys_addr, shadow_addr; - - pgt_info = kmalloc(sizeof(*pgt_info), GFP_KERNEL); - if (!pgt_info) - return ULLONG_MAX; - - phys_addr = (u64) gen_pool_alloc(hdev->mmu_pgt_pool, - prop->mmu_hop_table_size); - if (!phys_addr) { - dev_err(hdev->dev, "failed to allocate page\n"); - goto pool_add_err; - } - - shadow_addr = (u64) (uintptr_t) kzalloc(prop->mmu_hop_table_size, - GFP_KERNEL); - if (!shadow_addr) - goto shadow_err; - - pgt_info->phys_addr = phys_addr; - pgt_info->shadow_addr = shadow_addr; - pgt_info->ctx = ctx; - pgt_info->num_of_ptes = 0; - hash_add(ctx->mmu_shadow_hash, &pgt_info->node, shadow_addr); - - return shadow_addr; - -shadow_err: - gen_pool_free(hdev->mmu_pgt_pool, phys_addr, prop->mmu_hop_table_size); -pool_add_err: - kfree(pgt_info); - - return ULLONG_MAX; -} - -static inline u64 get_phys_hop0_addr(struct hl_ctx *ctx) -{ - return ctx->hdev->asic_prop.mmu_pgt_addr + - (ctx->asid * ctx->hdev->asic_prop.mmu_hop_table_size); -} - -static inline u64 get_hop0_addr(struct hl_ctx *ctx) -{ - return (u64) (uintptr_t) ctx->hdev->mmu_shadow_hop0 + - (ctx->asid * ctx->hdev->asic_prop.mmu_hop_table_size); -} - -static inline void flush(struct hl_ctx *ctx) -{ - /* flush all writes from all cores to reach PCI */ - mb(); - ctx->hdev->asic_funcs->read_pte(ctx->hdev, get_phys_hop0_addr(ctx)); -} - -/* transform the value to physical address when writing to H/W */ -static inline void write_pte(struct hl_ctx *ctx, u64 shadow_pte_addr, u64 val) -{ - /* - * The value to write is actually the address of the next shadow hop + - * flags at the 12 LSBs. - * Hence in order to get the value to write to the physical PTE, we - * clear the 12 LSBs and translate the shadow hop to its associated - * physical hop, and add back the original 12 LSBs. - */ - u64 phys_val = get_phys_addr(ctx, val & HOP_PHYS_ADDR_MASK) | - (val & FLAGS_MASK); - - ctx->hdev->asic_funcs->write_pte(ctx->hdev, - get_phys_addr(ctx, shadow_pte_addr), - phys_val); - - *(u64 *) (uintptr_t) shadow_pte_addr = val; -} - -/* do not transform the value to physical address when writing to H/W */ -static inline void write_final_pte(struct hl_ctx *ctx, u64 shadow_pte_addr, - u64 val) -{ - ctx->hdev->asic_funcs->write_pte(ctx->hdev, - get_phys_addr(ctx, shadow_pte_addr), - val); - *(u64 *) (uintptr_t) shadow_pte_addr = val; -} - -/* clear the last and present bits */ -static inline void clear_pte(struct hl_ctx *ctx, u64 pte_addr) -{ - /* no need to transform the value to physical address */ - write_final_pte(ctx, pte_addr, 0); -} - -static inline void get_pte(struct hl_ctx *ctx, u64 hop_addr) -{ - get_pgt_info(ctx, hop_addr)->num_of_ptes++; -} - -/* - * put_pte - decrement the num of ptes and free the hop if possible - * - * @ctx: pointer to the context structure - * @hop_addr: addr of the hop - * - * This function returns the number of ptes left on this hop. If the number is - * 0, it means the pte was freed. - */ -static inline int put_pte(struct hl_ctx *ctx, u64 hop_addr) -{ - struct pgt_info *pgt_info = get_pgt_info(ctx, hop_addr); - int num_of_ptes_left; - - pgt_info->num_of_ptes--; - - /* - * Need to save the number of ptes left because free_hop might free - * the pgt_info - */ - num_of_ptes_left = pgt_info->num_of_ptes; - if (!num_of_ptes_left) - _free_hop(ctx, pgt_info); - - return num_of_ptes_left; -} - -static inline u64 get_hopN_pte_addr(struct hl_ctx *ctx, u64 hop_addr, - u64 virt_addr, u64 mask, u64 shift) -{ - return hop_addr + ctx->hdev->asic_prop.mmu_pte_size * - ((virt_addr & mask) >> shift); -} - -static inline u64 get_hop0_pte_addr(struct hl_ctx *ctx, - struct hl_mmu_properties *mmu_prop, - u64 hop_addr, u64 vaddr) -{ - return get_hopN_pte_addr(ctx, hop_addr, vaddr, mmu_prop->hop0_mask, - mmu_prop->hop0_shift); -} - -static inline u64 get_hop1_pte_addr(struct hl_ctx *ctx, - struct hl_mmu_properties *mmu_prop, - u64 hop_addr, u64 vaddr) -{ - return get_hopN_pte_addr(ctx, hop_addr, vaddr, mmu_prop->hop1_mask, - mmu_prop->hop1_shift); -} - -static inline u64 get_hop2_pte_addr(struct hl_ctx *ctx, - struct hl_mmu_properties *mmu_prop, - u64 hop_addr, u64 vaddr) -{ - return get_hopN_pte_addr(ctx, hop_addr, vaddr, mmu_prop->hop2_mask, - mmu_prop->hop2_shift); -} - -static inline u64 get_hop3_pte_addr(struct hl_ctx *ctx, - struct hl_mmu_properties *mmu_prop, - u64 hop_addr, u64 vaddr) -{ - return get_hopN_pte_addr(ctx, hop_addr, vaddr, mmu_prop->hop3_mask, - mmu_prop->hop3_shift); -} - -static inline u64 get_hop4_pte_addr(struct hl_ctx *ctx, - struct hl_mmu_properties *mmu_prop, - u64 hop_addr, u64 vaddr) -{ - return get_hopN_pte_addr(ctx, hop_addr, vaddr, mmu_prop->hop4_mask, - mmu_prop->hop4_shift); -} - -static inline u64 get_next_hop_addr(struct hl_ctx *ctx, u64 curr_pte) -{ - if (curr_pte & PAGE_PRESENT_MASK) - return curr_pte & HOP_PHYS_ADDR_MASK; - else - return ULLONG_MAX; -} - -static inline u64 get_alloc_next_hop_addr(struct hl_ctx *ctx, u64 curr_pte, - bool *is_new_hop) -{ - u64 hop_addr = get_next_hop_addr(ctx, curr_pte); - - if (hop_addr == ULLONG_MAX) { - hop_addr = alloc_hop(ctx); - *is_new_hop = (hop_addr != ULLONG_MAX); - } - - return hop_addr; -} - -/* translates shadow address inside hop to a physical address */ -static inline u64 get_phys_addr(struct hl_ctx *ctx, u64 shadow_addr) -{ - u64 page_mask = (ctx->hdev->asic_prop.mmu_hop_table_size - 1); - u64 shadow_hop_addr = shadow_addr & ~page_mask; - u64 pte_offset = shadow_addr & page_mask; - u64 phys_hop_addr; - - if (shadow_hop_addr != get_hop0_addr(ctx)) - phys_hop_addr = get_pgt_info(ctx, shadow_hop_addr)->phys_addr; - else - phys_hop_addr = get_phys_hop0_addr(ctx); - - return phys_hop_addr + pte_offset; -} - -static bool is_dram_va(struct hl_device *hdev, u64 virt_addr) -{ - struct asic_fixed_properties *prop = &hdev->asic_prop; - - return hl_mem_area_inside_range(virt_addr, prop->dmmu.page_size, - prop->dmmu.start_addr, - prop->dmmu.end_addr); -} - -static int dram_default_mapping_init(struct hl_ctx *ctx) -{ - struct hl_device *hdev = ctx->hdev; - struct asic_fixed_properties *prop = &hdev->asic_prop; - u64 num_of_hop3, total_hops, hop0_addr, hop1_addr, hop2_addr, - hop2_pte_addr, hop3_pte_addr, pte_val; - int rc, i, j, hop3_allocated = 0; - - if ((!hdev->dram_supports_virtual_memory) || - (!hdev->dram_default_page_mapping) || - (ctx->asid == HL_KERNEL_ASID_ID)) - return 0; - - num_of_hop3 = prop->dram_size_for_default_page_mapping; - do_div(num_of_hop3, prop->dram_page_size); - do_div(num_of_hop3, PTE_ENTRIES_IN_HOP); - - /* add hop1 and hop2 */ - total_hops = num_of_hop3 + 2; - - ctx->dram_default_hops = kzalloc(HL_PTE_SIZE * total_hops, GFP_KERNEL); - if (!ctx->dram_default_hops) - return -ENOMEM; - - hop0_addr = get_hop0_addr(ctx); - - hop1_addr = alloc_hop(ctx); - if (hop1_addr == ULLONG_MAX) { - dev_err(hdev->dev, "failed to alloc hop 1\n"); - rc = -ENOMEM; - goto hop1_err; - } - - ctx->dram_default_hops[total_hops - 1] = hop1_addr; - - hop2_addr = alloc_hop(ctx); - if (hop2_addr == ULLONG_MAX) { - dev_err(hdev->dev, "failed to alloc hop 2\n"); - rc = -ENOMEM; - goto hop2_err; - } - - ctx->dram_default_hops[total_hops - 2] = hop2_addr; - - for (i = 0 ; i < num_of_hop3 ; i++) { - ctx->dram_default_hops[i] = alloc_hop(ctx); - if (ctx->dram_default_hops[i] == ULLONG_MAX) { - dev_err(hdev->dev, "failed to alloc hop 3, i: %d\n", i); - rc = -ENOMEM; - goto hop3_err; - } - hop3_allocated++; - } - - /* need only pte 0 in hops 0 and 1 */ - pte_val = (hop1_addr & HOP_PHYS_ADDR_MASK) | PAGE_PRESENT_MASK; - write_pte(ctx, hop0_addr, pte_val); - - pte_val = (hop2_addr & HOP_PHYS_ADDR_MASK) | PAGE_PRESENT_MASK; - write_pte(ctx, hop1_addr, pte_val); - get_pte(ctx, hop1_addr); - - hop2_pte_addr = hop2_addr; - for (i = 0 ; i < num_of_hop3 ; i++) { - pte_val = (ctx->dram_default_hops[i] & HOP_PHYS_ADDR_MASK) | - PAGE_PRESENT_MASK; - write_pte(ctx, hop2_pte_addr, pte_val); - get_pte(ctx, hop2_addr); - hop2_pte_addr += HL_PTE_SIZE; - } - - pte_val = (prop->mmu_dram_default_page_addr & HOP_PHYS_ADDR_MASK) | - LAST_MASK | PAGE_PRESENT_MASK; - - for (i = 0 ; i < num_of_hop3 ; i++) { - hop3_pte_addr = ctx->dram_default_hops[i]; - for (j = 0 ; j < PTE_ENTRIES_IN_HOP ; j++) { - write_final_pte(ctx, hop3_pte_addr, pte_val); - get_pte(ctx, ctx->dram_default_hops[i]); - hop3_pte_addr += HL_PTE_SIZE; - } - } - - flush(ctx); - - return 0; - -hop3_err: - for (i = 0 ; i < hop3_allocated ; i++) - free_hop(ctx, ctx->dram_default_hops[i]); - - free_hop(ctx, hop2_addr); -hop2_err: - free_hop(ctx, hop1_addr); -hop1_err: - kfree(ctx->dram_default_hops); - - return rc; -} - -static void dram_default_mapping_fini(struct hl_ctx *ctx) -{ - struct hl_device *hdev = ctx->hdev; - struct asic_fixed_properties *prop = &hdev->asic_prop; - u64 num_of_hop3, total_hops, hop0_addr, hop1_addr, hop2_addr, - hop2_pte_addr, hop3_pte_addr; - int i, j; - - if ((!hdev->dram_supports_virtual_memory) || - (!hdev->dram_default_page_mapping) || - (ctx->asid == HL_KERNEL_ASID_ID)) - return; - - num_of_hop3 = prop->dram_size_for_default_page_mapping; - do_div(num_of_hop3, prop->dram_page_size); - do_div(num_of_hop3, PTE_ENTRIES_IN_HOP); - - hop0_addr = get_hop0_addr(ctx); - /* add hop1 and hop2 */ - total_hops = num_of_hop3 + 2; - hop1_addr = ctx->dram_default_hops[total_hops - 1]; - hop2_addr = ctx->dram_default_hops[total_hops - 2]; - - for (i = 0 ; i < num_of_hop3 ; i++) { - hop3_pte_addr = ctx->dram_default_hops[i]; - for (j = 0 ; j < PTE_ENTRIES_IN_HOP ; j++) { - clear_pte(ctx, hop3_pte_addr); - put_pte(ctx, ctx->dram_default_hops[i]); - hop3_pte_addr += HL_PTE_SIZE; - } - } - - hop2_pte_addr = hop2_addr; - hop2_pte_addr = hop2_addr; - for (i = 0 ; i < num_of_hop3 ; i++) { - clear_pte(ctx, hop2_pte_addr); - put_pte(ctx, hop2_addr); - hop2_pte_addr += HL_PTE_SIZE; - } - - clear_pte(ctx, hop1_addr); - put_pte(ctx, hop1_addr); - clear_pte(ctx, hop0_addr); - - kfree(ctx->dram_default_hops); - - flush(ctx); -} - -/** - * hl_mmu_init() - initialize the MMU module. - * @hdev: habanalabs device structure. - * - * This function does the following: - * - Create a pool of pages for pgt_infos. - * - Create a shadow table for pgt - * - * Return: 0 for success, non-zero for failure. - */ -int hl_mmu_init(struct hl_device *hdev) -{ - struct asic_fixed_properties *prop = &hdev->asic_prop; - int rc; - - if (!hdev->mmu_enable) - return 0; - - hdev->mmu_pgt_pool = - gen_pool_create(__ffs(prop->mmu_hop_table_size), -1); - - if (!hdev->mmu_pgt_pool) { - dev_err(hdev->dev, "Failed to create page gen pool\n"); - return -ENOMEM; - } - - rc = gen_pool_add(hdev->mmu_pgt_pool, prop->mmu_pgt_addr + - prop->mmu_hop0_tables_total_size, - prop->mmu_pgt_size - prop->mmu_hop0_tables_total_size, - -1); - if (rc) { - dev_err(hdev->dev, "Failed to add memory to page gen pool\n"); - goto err_pool_add; - } - - hdev->mmu_shadow_hop0 = kvmalloc_array(prop->max_asid, - prop->mmu_hop_table_size, - GFP_KERNEL | __GFP_ZERO); - if (!hdev->mmu_shadow_hop0) { - rc = -ENOMEM; - goto err_pool_add; - } - - /* MMU H/W init will be done in device hw_init() */ - - return 0; - -err_pool_add: - gen_pool_destroy(hdev->mmu_pgt_pool); - - return rc; -} - -/** - * hl_mmu_fini() - release the MMU module. - * @hdev: habanalabs device structure. - * - * This function does the following: - * - Disable MMU in H/W. - * - Free the pgt_infos pool. - * - * All contexts should be freed before calling this function. - */ -void hl_mmu_fini(struct hl_device *hdev) -{ - if (!hdev->mmu_enable) - return; - - /* MMU H/W fini was already done in device hw_fini() */ - - kvfree(hdev->mmu_shadow_hop0); - gen_pool_destroy(hdev->mmu_pgt_pool); -} - -/** - * hl_mmu_ctx_init() - initialize a context for using the MMU module. - * @ctx: pointer to the context structure to initialize. - * - * Initialize a mutex to protect the concurrent mapping flow, a hash to hold all - * page tables hops related to this context. - * Return: 0 on success, non-zero otherwise. - */ -int hl_mmu_ctx_init(struct hl_ctx *ctx) -{ - struct hl_device *hdev = ctx->hdev; - - if (!hdev->mmu_enable) - return 0; - - mutex_init(&ctx->mmu_lock); - hash_init(ctx->mmu_shadow_hash); - - return dram_default_mapping_init(ctx); -} - -/* - * hl_mmu_ctx_fini - disable a ctx from using the mmu module - * - * @ctx: pointer to the context structure - * - * This function does the following: - * - Free any pgts which were not freed yet - * - Free the mutex - * - Free DRAM default page mapping hops - */ -void hl_mmu_ctx_fini(struct hl_ctx *ctx) -{ - struct hl_device *hdev = ctx->hdev; - struct pgt_info *pgt_info; - struct hlist_node *tmp; - int i; - - if (!hdev->mmu_enable) - return; - - dram_default_mapping_fini(ctx); - - if (!hash_empty(ctx->mmu_shadow_hash)) - dev_err(hdev->dev, "ctx %d is freed while it has pgts in use\n", - ctx->asid); - - hash_for_each_safe(ctx->mmu_shadow_hash, i, tmp, pgt_info, node) { - dev_err_ratelimited(hdev->dev, - "pgt_info of addr 0x%llx of asid %d was not destroyed, num_ptes: %d\n", - pgt_info->phys_addr, ctx->asid, pgt_info->num_of_ptes); - _free_hop(ctx, pgt_info); - } - - mutex_destroy(&ctx->mmu_lock); -} - -static int _hl_mmu_unmap(struct hl_ctx *ctx, u64 virt_addr, bool is_dram_addr) -{ - struct hl_device *hdev = ctx->hdev; - struct asic_fixed_properties *prop = &hdev->asic_prop; - struct hl_mmu_properties *mmu_prop; - u64 hop0_addr = 0, hop0_pte_addr = 0, - hop1_addr = 0, hop1_pte_addr = 0, - hop2_addr = 0, hop2_pte_addr = 0, - hop3_addr = 0, hop3_pte_addr = 0, - hop4_addr = 0, hop4_pte_addr = 0, - curr_pte; - bool is_huge, clear_hop3 = true; - - /* shifts and masks are the same in PMMU and HPMMU, use one of them */ - mmu_prop = is_dram_addr ? &prop->dmmu : &prop->pmmu; - - hop0_addr = get_hop0_addr(ctx); - hop0_pte_addr = get_hop0_pte_addr(ctx, mmu_prop, hop0_addr, virt_addr); - - curr_pte = *(u64 *) (uintptr_t) hop0_pte_addr; - - hop1_addr = get_next_hop_addr(ctx, curr_pte); - - if (hop1_addr == ULLONG_MAX) - goto not_mapped; - - hop1_pte_addr = get_hop1_pte_addr(ctx, mmu_prop, hop1_addr, virt_addr); - - curr_pte = *(u64 *) (uintptr_t) hop1_pte_addr; - - hop2_addr = get_next_hop_addr(ctx, curr_pte); - - if (hop2_addr == ULLONG_MAX) - goto not_mapped; - - hop2_pte_addr = get_hop2_pte_addr(ctx, mmu_prop, hop2_addr, virt_addr); - - curr_pte = *(u64 *) (uintptr_t) hop2_pte_addr; - - hop3_addr = get_next_hop_addr(ctx, curr_pte); - - if (hop3_addr == ULLONG_MAX) - goto not_mapped; - - hop3_pte_addr = get_hop3_pte_addr(ctx, mmu_prop, hop3_addr, virt_addr); - - curr_pte = *(u64 *) (uintptr_t) hop3_pte_addr; - - is_huge = curr_pte & LAST_MASK; - - if (is_dram_addr && !is_huge) { - dev_err(hdev->dev, - "DRAM unmapping should use huge pages only\n"); - return -EFAULT; - } - - if (!is_huge) { - hop4_addr = get_next_hop_addr(ctx, curr_pte); - - if (hop4_addr == ULLONG_MAX) - goto not_mapped; - - hop4_pte_addr = get_hop4_pte_addr(ctx, mmu_prop, hop4_addr, - virt_addr); - - curr_pte = *(u64 *) (uintptr_t) hop4_pte_addr; - - clear_hop3 = false; - } - - if (hdev->dram_default_page_mapping && is_dram_addr) { - u64 default_pte = (prop->mmu_dram_default_page_addr & - HOP_PHYS_ADDR_MASK) | LAST_MASK | - PAGE_PRESENT_MASK; - if (curr_pte == default_pte) { - dev_err(hdev->dev, - "DRAM: hop3 PTE points to zero page, can't unmap, va: 0x%llx\n", - virt_addr); - goto not_mapped; - } - - if (!(curr_pte & PAGE_PRESENT_MASK)) { - dev_err(hdev->dev, - "DRAM: hop3 PTE is cleared! can't unmap, va: 0x%llx\n", - virt_addr); - goto not_mapped; - } - - write_final_pte(ctx, hop3_pte_addr, default_pte); - put_pte(ctx, hop3_addr); - } else { - if (!(curr_pte & PAGE_PRESENT_MASK)) - goto not_mapped; - - if (hop4_addr) - clear_pte(ctx, hop4_pte_addr); - else - clear_pte(ctx, hop3_pte_addr); - - if (hop4_addr && !put_pte(ctx, hop4_addr)) - clear_hop3 = true; - - if (!clear_hop3) - goto mapped; - - clear_pte(ctx, hop3_pte_addr); - - if (put_pte(ctx, hop3_addr)) - goto mapped; - - clear_pte(ctx, hop2_pte_addr); - - if (put_pte(ctx, hop2_addr)) - goto mapped; - - clear_pte(ctx, hop1_pte_addr); - - if (put_pte(ctx, hop1_addr)) - goto mapped; - - clear_pte(ctx, hop0_pte_addr); - } - -mapped: - return 0; - -not_mapped: - dev_err(hdev->dev, "virt addr 0x%llx is not mapped to phys addr\n", - virt_addr); - - return -EINVAL; -} - -/* - * hl_mmu_unmap - unmaps a virtual addr - * - * @ctx: pointer to the context structure - * @virt_addr: virt addr to map from - * @page_size: size of the page to unmap - * @flush_pte: whether to do a PCI flush - * - * This function does the following: - * - Check that the virt addr is mapped - * - Unmap the virt addr and frees pgts if possible - * - Returns 0 on success, -EINVAL if the given addr is not mapped - * - * Because this function changes the page tables in the device and because it - * changes the MMU hash, it must be protected by a lock. - * However, because it maps only a single page, the lock should be implemented - * in a higher level in order to protect the entire mapping of the memory area - * - * For optimization reasons PCI flush may be requested once after unmapping of - * large area. - */ -int hl_mmu_unmap(struct hl_ctx *ctx, u64 virt_addr, u32 page_size, - bool flush_pte) -{ - struct hl_device *hdev = ctx->hdev; - struct asic_fixed_properties *prop = &hdev->asic_prop; - struct hl_mmu_properties *mmu_prop; - u64 real_virt_addr; - u32 real_page_size, npages; - int i, rc = 0; - bool is_dram_addr; - - if (!hdev->mmu_enable) - return 0; - - is_dram_addr = is_dram_va(hdev, virt_addr); - - if (is_dram_addr) - mmu_prop = &prop->dmmu; - else if ((page_size % prop->pmmu_huge.page_size) == 0) - mmu_prop = &prop->pmmu_huge; - else - mmu_prop = &prop->pmmu; - - /* - * The H/W handles mapping of specific page sizes. Hence if the page - * size is bigger, we break it to sub-pages and unmap them separately. - */ - if ((page_size % mmu_prop->page_size) == 0) { - real_page_size = mmu_prop->page_size; - } else { - dev_err(hdev->dev, - "page size of %u is not %uKB aligned, can't unmap\n", - page_size, mmu_prop->page_size >> 10); - - return -EFAULT; - } - - npages = page_size / real_page_size; - real_virt_addr = virt_addr; - - for (i = 0 ; i < npages ; i++) { - rc = _hl_mmu_unmap(ctx, real_virt_addr, is_dram_addr); - if (rc) - break; - - real_virt_addr += real_page_size; - } - - if (flush_pte) - flush(ctx); - - return rc; -} - -static int _hl_mmu_map(struct hl_ctx *ctx, u64 virt_addr, u64 phys_addr, - u32 page_size, bool is_dram_addr) -{ - struct hl_device *hdev = ctx->hdev; - struct asic_fixed_properties *prop = &hdev->asic_prop; - struct hl_mmu_properties *mmu_prop; - u64 hop0_addr = 0, hop0_pte_addr = 0, - hop1_addr = 0, hop1_pte_addr = 0, - hop2_addr = 0, hop2_pte_addr = 0, - hop3_addr = 0, hop3_pte_addr = 0, - hop4_addr = 0, hop4_pte_addr = 0, - curr_pte = 0; - bool hop1_new = false, hop2_new = false, hop3_new = false, - hop4_new = false, is_huge; - int rc = -ENOMEM; - - /* - * This mapping function can map a page or a huge page. For huge page - * there are only 3 hops rather than 4. Currently the DRAM allocation - * uses huge pages only but user memory could have been allocated with - * one of the two page sizes. Since this is a common code for all the - * three cases, we need this hugs page check. - */ - if (is_dram_addr) { - mmu_prop = &prop->dmmu; - is_huge = true; - } else if (page_size == prop->pmmu_huge.page_size) { - mmu_prop = &prop->pmmu_huge; - is_huge = true; - } else { - mmu_prop = &prop->pmmu; - is_huge = false; - } - - hop0_addr = get_hop0_addr(ctx); - hop0_pte_addr = get_hop0_pte_addr(ctx, mmu_prop, hop0_addr, virt_addr); - curr_pte = *(u64 *) (uintptr_t) hop0_pte_addr; - - hop1_addr = get_alloc_next_hop_addr(ctx, curr_pte, &hop1_new); - if (hop1_addr == ULLONG_MAX) - goto err; - - hop1_pte_addr = get_hop1_pte_addr(ctx, mmu_prop, hop1_addr, virt_addr); - curr_pte = *(u64 *) (uintptr_t) hop1_pte_addr; - - hop2_addr = get_alloc_next_hop_addr(ctx, curr_pte, &hop2_new); - if (hop2_addr == ULLONG_MAX) - goto err; - - hop2_pte_addr = get_hop2_pte_addr(ctx, mmu_prop, hop2_addr, virt_addr); - curr_pte = *(u64 *) (uintptr_t) hop2_pte_addr; - - hop3_addr = get_alloc_next_hop_addr(ctx, curr_pte, &hop3_new); - if (hop3_addr == ULLONG_MAX) - goto err; - - hop3_pte_addr = get_hop3_pte_addr(ctx, mmu_prop, hop3_addr, virt_addr); - curr_pte = *(u64 *) (uintptr_t) hop3_pte_addr; - - if (!is_huge) { - hop4_addr = get_alloc_next_hop_addr(ctx, curr_pte, &hop4_new); - if (hop4_addr == ULLONG_MAX) - goto err; - - hop4_pte_addr = get_hop4_pte_addr(ctx, mmu_prop, hop4_addr, - virt_addr); - curr_pte = *(u64 *) (uintptr_t) hop4_pte_addr; - } - - if (hdev->dram_default_page_mapping && is_dram_addr) { - u64 default_pte = (prop->mmu_dram_default_page_addr & - HOP_PHYS_ADDR_MASK) | LAST_MASK | - PAGE_PRESENT_MASK; - - if (curr_pte != default_pte) { - dev_err(hdev->dev, - "DRAM: mapping already exists for virt_addr 0x%llx\n", - virt_addr); - rc = -EINVAL; - goto err; - } - - if (hop1_new || hop2_new || hop3_new || hop4_new) { - dev_err(hdev->dev, - "DRAM mapping should not allocate more hops\n"); - rc = -EFAULT; - goto err; - } - } else if (curr_pte & PAGE_PRESENT_MASK) { - dev_err(hdev->dev, - "mapping already exists for virt_addr 0x%llx\n", - virt_addr); - - dev_dbg(hdev->dev, "hop0 pte: 0x%llx (0x%llx)\n", - *(u64 *) (uintptr_t) hop0_pte_addr, hop0_pte_addr); - dev_dbg(hdev->dev, "hop1 pte: 0x%llx (0x%llx)\n", - *(u64 *) (uintptr_t) hop1_pte_addr, hop1_pte_addr); - dev_dbg(hdev->dev, "hop2 pte: 0x%llx (0x%llx)\n", - *(u64 *) (uintptr_t) hop2_pte_addr, hop2_pte_addr); - dev_dbg(hdev->dev, "hop3 pte: 0x%llx (0x%llx)\n", - *(u64 *) (uintptr_t) hop3_pte_addr, hop3_pte_addr); - - if (!is_huge) - dev_dbg(hdev->dev, "hop4 pte: 0x%llx (0x%llx)\n", - *(u64 *) (uintptr_t) hop4_pte_addr, - hop4_pte_addr); - - rc = -EINVAL; - goto err; - } - - curr_pte = (phys_addr & HOP_PHYS_ADDR_MASK) | LAST_MASK - | PAGE_PRESENT_MASK; - - if (is_huge) - write_final_pte(ctx, hop3_pte_addr, curr_pte); - else - write_final_pte(ctx, hop4_pte_addr, curr_pte); - - if (hop1_new) { - curr_pte = - (hop1_addr & HOP_PHYS_ADDR_MASK) | PAGE_PRESENT_MASK; - write_pte(ctx, hop0_pte_addr, curr_pte); - } - if (hop2_new) { - curr_pte = - (hop2_addr & HOP_PHYS_ADDR_MASK) | PAGE_PRESENT_MASK; - write_pte(ctx, hop1_pte_addr, curr_pte); - get_pte(ctx, hop1_addr); - } - if (hop3_new) { - curr_pte = - (hop3_addr & HOP_PHYS_ADDR_MASK) | PAGE_PRESENT_MASK; - write_pte(ctx, hop2_pte_addr, curr_pte); - get_pte(ctx, hop2_addr); - } - - if (!is_huge) { - if (hop4_new) { - curr_pte = (hop4_addr & HOP_PHYS_ADDR_MASK) | - PAGE_PRESENT_MASK; - write_pte(ctx, hop3_pte_addr, curr_pte); - get_pte(ctx, hop3_addr); - } - - get_pte(ctx, hop4_addr); - } else { - get_pte(ctx, hop3_addr); - } - - return 0; - -err: - if (hop4_new) - free_hop(ctx, hop4_addr); - if (hop3_new) - free_hop(ctx, hop3_addr); - if (hop2_new) - free_hop(ctx, hop2_addr); - if (hop1_new) - free_hop(ctx, hop1_addr); - - return rc; -} - -/* - * hl_mmu_map - maps a virtual addr to physical addr - * - * @ctx: pointer to the context structure - * @virt_addr: virt addr to map from - * @phys_addr: phys addr to map to - * @page_size: physical page size - * @flush_pte: whether to do a PCI flush - * - * This function does the following: - * - Check that the virt addr is not mapped - * - Allocate pgts as necessary in order to map the virt addr to the phys - * - Returns 0 on success, -EINVAL if addr is already mapped, or -ENOMEM. - * - * Because this function changes the page tables in the device and because it - * changes the MMU hash, it must be protected by a lock. - * However, because it maps only a single page, the lock should be implemented - * in a higher level in order to protect the entire mapping of the memory area - * - * For optimization reasons PCI flush may be requested once after mapping of - * large area. - */ -int hl_mmu_map(struct hl_ctx *ctx, u64 virt_addr, u64 phys_addr, u32 page_size, - bool flush_pte) -{ - struct hl_device *hdev = ctx->hdev; - struct asic_fixed_properties *prop = &hdev->asic_prop; - struct hl_mmu_properties *mmu_prop; - u64 real_virt_addr, real_phys_addr; - u32 real_page_size, npages; - int i, rc, mapped_cnt = 0; - bool is_dram_addr; - - if (!hdev->mmu_enable) - return 0; - - is_dram_addr = is_dram_va(hdev, virt_addr); - - if (is_dram_addr) - mmu_prop = &prop->dmmu; - else if ((page_size % prop->pmmu_huge.page_size) == 0) - mmu_prop = &prop->pmmu_huge; - else - mmu_prop = &prop->pmmu; - - /* - * The H/W handles mapping of specific page sizes. Hence if the page - * size is bigger, we break it to sub-pages and map them separately. - */ - if ((page_size % mmu_prop->page_size) == 0) { - real_page_size = mmu_prop->page_size; - } else { - dev_err(hdev->dev, - "page size of %u is not %uKB aligned, can't unmap\n", - page_size, mmu_prop->page_size >> 10); - - return -EFAULT; - } - - WARN_ONCE((phys_addr & (real_page_size - 1)), - "Mapping 0x%llx with page size of 0x%x is erroneous! Address must be divisible by page size", - phys_addr, real_page_size); - - npages = page_size / real_page_size; - real_virt_addr = virt_addr; - real_phys_addr = phys_addr; - - for (i = 0 ; i < npages ; i++) { - rc = _hl_mmu_map(ctx, real_virt_addr, real_phys_addr, - real_page_size, is_dram_addr); - if (rc) - goto err; - - real_virt_addr += real_page_size; - real_phys_addr += real_page_size; - mapped_cnt++; - } - - if (flush_pte) - flush(ctx); - - return 0; - -err: - real_virt_addr = virt_addr; - for (i = 0 ; i < mapped_cnt ; i++) { - if (_hl_mmu_unmap(ctx, real_virt_addr, is_dram_addr)) - dev_warn_ratelimited(hdev->dev, - "failed to unmap va: 0x%llx\n", real_virt_addr); - - real_virt_addr += real_page_size; - } - - flush(ctx); - - return rc; -} - -/* - * hl_mmu_swap_out - marks all mapping of the given ctx as swapped out - * - * @ctx: pointer to the context structure - * - */ -void hl_mmu_swap_out(struct hl_ctx *ctx) -{ - -} - -/* - * hl_mmu_swap_in - marks all mapping of the given ctx as swapped in - * - * @ctx: pointer to the context structure - * - */ -void hl_mmu_swap_in(struct hl_ctx *ctx) -{ - -} diff --git a/drivers/misc/habanalabs/pci.c b/drivers/misc/habanalabs/pci.c deleted file mode 100644 index 1791f6623c69..000000000000 --- a/drivers/misc/habanalabs/pci.c +++ /dev/null @@ -1,400 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -/* - * Copyright 2016-2019 HabanaLabs, Ltd. - * All Rights Reserved. - */ - -#include "habanalabs.h" -#include "include/hw_ip/pci/pci_general.h" - -#include -#include - -#define HL_PLDM_PCI_ELBI_TIMEOUT_MSEC (HL_PCI_ELBI_TIMEOUT_MSEC * 10) - -#define IATU_REGION_CTRL_REGION_EN_MASK BIT(31) -#define IATU_REGION_CTRL_MATCH_MODE_MASK BIT(30) -#define IATU_REGION_CTRL_NUM_MATCH_EN_MASK BIT(19) -#define IATU_REGION_CTRL_BAR_NUM_MASK GENMASK(10, 8) - -/** - * hl_pci_bars_map() - Map PCI BARs. - * @hdev: Pointer to hl_device structure. - * @name: Array of BAR names. - * @is_wc: Array with flag per BAR whether a write-combined mapping is needed. - * - * Request PCI regions and map them to kernel virtual addresses. - * - * Return: 0 on success, non-zero for failure. - */ -int hl_pci_bars_map(struct hl_device *hdev, const char * const name[3], - bool is_wc[3]) -{ - struct pci_dev *pdev = hdev->pdev; - int rc, i, bar; - - rc = pci_request_regions(pdev, HL_NAME); - if (rc) { - dev_err(hdev->dev, "Cannot obtain PCI resources\n"); - return rc; - } - - for (i = 0 ; i < 3 ; i++) { - bar = i * 2; /* 64-bit BARs */ - hdev->pcie_bar[bar] = is_wc[i] ? - pci_ioremap_wc_bar(pdev, bar) : - pci_ioremap_bar(pdev, bar); - if (!hdev->pcie_bar[bar]) { - dev_err(hdev->dev, "pci_ioremap%s_bar failed for %s\n", - is_wc[i] ? "_wc" : "", name[i]); - rc = -ENODEV; - goto err; - } - } - - return 0; - -err: - for (i = 2 ; i >= 0 ; i--) { - bar = i * 2; /* 64-bit BARs */ - if (hdev->pcie_bar[bar]) - iounmap(hdev->pcie_bar[bar]); - } - - pci_release_regions(pdev); - - return rc; -} - -/** - * hl_pci_bars_unmap() - Unmap PCI BARS. - * @hdev: Pointer to hl_device structure. - * - * Release all PCI BARs and unmap their virtual addresses. - */ -static void hl_pci_bars_unmap(struct hl_device *hdev) -{ - struct pci_dev *pdev = hdev->pdev; - int i, bar; - - for (i = 2 ; i >= 0 ; i--) { - bar = i * 2; /* 64-bit BARs */ - iounmap(hdev->pcie_bar[bar]); - } - - pci_release_regions(pdev); -} - -/** - * hl_pci_elbi_write() - Write through the ELBI interface. - * @hdev: Pointer to hl_device structure. - * @addr: Address to write to - * @data: Data to write - * - * Return: 0 on success, negative value for failure. - */ -static int hl_pci_elbi_write(struct hl_device *hdev, u64 addr, u32 data) -{ - struct pci_dev *pdev = hdev->pdev; - ktime_t timeout; - u64 msec; - u32 val; - - if (hdev->pldm) - msec = HL_PLDM_PCI_ELBI_TIMEOUT_MSEC; - else - msec = HL_PCI_ELBI_TIMEOUT_MSEC; - - /* Clear previous status */ - pci_write_config_dword(pdev, mmPCI_CONFIG_ELBI_STS, 0); - - pci_write_config_dword(pdev, mmPCI_CONFIG_ELBI_ADDR, (u32) addr); - pci_write_config_dword(pdev, mmPCI_CONFIG_ELBI_DATA, data); - pci_write_config_dword(pdev, mmPCI_CONFIG_ELBI_CTRL, - PCI_CONFIG_ELBI_CTRL_WRITE); - - timeout = ktime_add_ms(ktime_get(), msec); - for (;;) { - pci_read_config_dword(pdev, mmPCI_CONFIG_ELBI_STS, &val); - if (val & PCI_CONFIG_ELBI_STS_MASK) - break; - if (ktime_compare(ktime_get(), timeout) > 0) { - pci_read_config_dword(pdev, mmPCI_CONFIG_ELBI_STS, - &val); - break; - } - - usleep_range(300, 500); - } - - if ((val & PCI_CONFIG_ELBI_STS_MASK) == PCI_CONFIG_ELBI_STS_DONE) - return 0; - - if (val & PCI_CONFIG_ELBI_STS_ERR) { - dev_err(hdev->dev, "Error writing to ELBI\n"); - return -EIO; - } - - if (!(val & PCI_CONFIG_ELBI_STS_MASK)) { - dev_err(hdev->dev, "ELBI write didn't finish in time\n"); - return -EIO; - } - - dev_err(hdev->dev, "ELBI write has undefined bits in status\n"); - return -EIO; -} - -/** - * hl_pci_iatu_write() - iatu write routine. - * @hdev: Pointer to hl_device structure. - * @addr: Address to write to - * @data: Data to write - * - * Return: 0 on success, negative value for failure. - */ -int hl_pci_iatu_write(struct hl_device *hdev, u32 addr, u32 data) -{ - struct asic_fixed_properties *prop = &hdev->asic_prop; - u32 dbi_offset; - int rc; - - dbi_offset = addr & 0xFFF; - - rc = hl_pci_elbi_write(hdev, prop->pcie_aux_dbi_reg_addr, 0x00300000); - rc |= hl_pci_elbi_write(hdev, prop->pcie_dbi_base_address + dbi_offset, - data); - - if (rc) - return -EIO; - - return 0; -} - -/** - * hl_pci_reset_link_through_bridge() - Reset PCI link. - * @hdev: Pointer to hl_device structure. - */ -static void hl_pci_reset_link_through_bridge(struct hl_device *hdev) -{ - struct pci_dev *pdev = hdev->pdev; - struct pci_dev *parent_port; - u16 val; - - parent_port = pdev->bus->self; - pci_read_config_word(parent_port, PCI_BRIDGE_CONTROL, &val); - val |= PCI_BRIDGE_CTL_BUS_RESET; - pci_write_config_word(parent_port, PCI_BRIDGE_CONTROL, val); - ssleep(1); - - val &= ~(PCI_BRIDGE_CTL_BUS_RESET); - pci_write_config_word(parent_port, PCI_BRIDGE_CONTROL, val); - ssleep(3); -} - -/** - * hl_pci_set_inbound_region() - Configure inbound region - * @hdev: Pointer to hl_device structure. - * @region: Inbound region number. - * @pci_region: Inbound region parameters. - * - * Configure the iATU inbound region. - * - * Return: 0 on success, negative value for failure. - */ -int hl_pci_set_inbound_region(struct hl_device *hdev, u8 region, - struct hl_inbound_pci_region *pci_region) -{ - struct asic_fixed_properties *prop = &hdev->asic_prop; - u64 bar_phys_base, region_base, region_end_address; - u32 offset, ctrl_reg_val; - int rc = 0; - - /* region offset */ - offset = (0x200 * region) + 0x100; - - if (pci_region->mode == PCI_ADDRESS_MATCH_MODE) { - bar_phys_base = hdev->pcie_bar_phys[pci_region->bar]; - region_base = bar_phys_base + pci_region->offset_in_bar; - region_end_address = region_base + pci_region->size - 1; - - rc |= hl_pci_iatu_write(hdev, offset + 0x8, - lower_32_bits(region_base)); - rc |= hl_pci_iatu_write(hdev, offset + 0xC, - upper_32_bits(region_base)); - rc |= hl_pci_iatu_write(hdev, offset + 0x10, - lower_32_bits(region_end_address)); - } - - /* Point to the specified address */ - rc = hl_pci_iatu_write(hdev, offset + 0x14, - lower_32_bits(pci_region->addr)); - rc |= hl_pci_iatu_write(hdev, offset + 0x18, - upper_32_bits(pci_region->addr)); - rc |= hl_pci_iatu_write(hdev, offset + 0x0, 0); - - /* Enable + bar/address match + match enable + bar number */ - ctrl_reg_val = FIELD_PREP(IATU_REGION_CTRL_REGION_EN_MASK, 1); - ctrl_reg_val |= FIELD_PREP(IATU_REGION_CTRL_MATCH_MODE_MASK, - pci_region->mode); - ctrl_reg_val |= FIELD_PREP(IATU_REGION_CTRL_NUM_MATCH_EN_MASK, 1); - - if (pci_region->mode == PCI_BAR_MATCH_MODE) - ctrl_reg_val |= FIELD_PREP(IATU_REGION_CTRL_BAR_NUM_MASK, - pci_region->bar); - - rc |= hl_pci_iatu_write(hdev, offset + 0x4, ctrl_reg_val); - - /* Return the DBI window to the default location */ - rc |= hl_pci_elbi_write(hdev, prop->pcie_aux_dbi_reg_addr, 0); - rc |= hl_pci_elbi_write(hdev, prop->pcie_aux_dbi_reg_addr + 4, 0); - - if (rc) - dev_err(hdev->dev, "failed to map bar %u to 0x%08llx\n", - pci_region->bar, pci_region->addr); - - return rc; -} - -/** - * hl_pci_set_outbound_region() - Configure outbound region 0 - * @hdev: Pointer to hl_device structure. - * @pci_region: Outbound region parameters. - * - * Configure the iATU outbound region 0. - * - * Return: 0 on success, negative value for failure. - */ -int hl_pci_set_outbound_region(struct hl_device *hdev, - struct hl_outbound_pci_region *pci_region) -{ - struct asic_fixed_properties *prop = &hdev->asic_prop; - u64 outbound_region_end_address; - int rc = 0; - - /* Outbound Region 0 */ - outbound_region_end_address = - pci_region->addr + pci_region->size - 1; - rc |= hl_pci_iatu_write(hdev, 0x008, - lower_32_bits(pci_region->addr)); - rc |= hl_pci_iatu_write(hdev, 0x00C, - upper_32_bits(pci_region->addr)); - rc |= hl_pci_iatu_write(hdev, 0x010, - lower_32_bits(outbound_region_end_address)); - rc |= hl_pci_iatu_write(hdev, 0x014, 0); - - if ((hdev->power9_64bit_dma_enable) && (hdev->dma_mask == 64)) - rc |= hl_pci_iatu_write(hdev, 0x018, 0x08000000); - else - rc |= hl_pci_iatu_write(hdev, 0x018, 0); - - rc |= hl_pci_iatu_write(hdev, 0x020, - upper_32_bits(outbound_region_end_address)); - /* Increase region size */ - rc |= hl_pci_iatu_write(hdev, 0x000, 0x00002000); - /* Enable */ - rc |= hl_pci_iatu_write(hdev, 0x004, 0x80000000); - - /* Return the DBI window to the default location */ - rc |= hl_pci_elbi_write(hdev, prop->pcie_aux_dbi_reg_addr, 0); - rc |= hl_pci_elbi_write(hdev, prop->pcie_aux_dbi_reg_addr + 4, 0); - - return rc; -} - -/** - * hl_pci_set_dma_mask() - Set DMA masks for the device. - * @hdev: Pointer to hl_device structure. - * - * This function sets the DMA masks (regular and consistent) for a specified - * value. If it doesn't succeed, it tries to set it to a fall-back value - * - * Return: 0 on success, non-zero for failure. - */ -static int hl_pci_set_dma_mask(struct hl_device *hdev) -{ - struct pci_dev *pdev = hdev->pdev; - int rc; - - /* set DMA mask */ - rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(hdev->dma_mask)); - if (rc) { - dev_err(hdev->dev, - "Failed to set pci dma mask to %d bits, error %d\n", - hdev->dma_mask, rc); - return rc; - } - - rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(hdev->dma_mask)); - if (rc) { - dev_err(hdev->dev, - "Failed to set pci consistent dma mask to %d bits, error %d\n", - hdev->dma_mask, rc); - return rc; - } - - return 0; -} - -/** - * hl_pci_init() - PCI initialization code. - * @hdev: Pointer to hl_device structure. - * - * Set DMA masks, initialize the PCI controller and map the PCI BARs. - * - * Return: 0 on success, non-zero for failure. - */ -int hl_pci_init(struct hl_device *hdev) -{ - struct pci_dev *pdev = hdev->pdev; - int rc; - - if (hdev->reset_pcilink) - hl_pci_reset_link_through_bridge(hdev); - - rc = pci_enable_device_mem(pdev); - if (rc) { - dev_err(hdev->dev, "can't enable PCI device\n"); - return rc; - } - - pci_set_master(pdev); - - rc = hdev->asic_funcs->pci_bars_map(hdev); - if (rc) { - dev_err(hdev->dev, "Failed to initialize PCI BARs\n"); - goto disable_device; - } - - rc = hdev->asic_funcs->init_iatu(hdev); - if (rc) { - dev_err(hdev->dev, "Failed to initialize iATU\n"); - goto disable_device; - } - - rc = hl_pci_set_dma_mask(hdev); - if (rc) - goto disable_device; - - return 0; - -disable_device: - pci_clear_master(pdev); - pci_disable_device(pdev); - - return rc; -} - -/** - * hl_fw_fini() - PCI finalization code. - * @hdev: Pointer to hl_device structure - * - * Unmap PCI bars and disable PCI device. - */ -void hl_pci_fini(struct hl_device *hdev) -{ - hl_pci_bars_unmap(hdev); - - pci_clear_master(hdev->pdev); - pci_disable_device(hdev->pdev); -} diff --git a/drivers/misc/habanalabs/sysfs.c b/drivers/misc/habanalabs/sysfs.c deleted file mode 100644 index 5d78d5e1c782..000000000000 --- a/drivers/misc/habanalabs/sysfs.c +++ /dev/null @@ -1,442 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -/* - * Copyright 2016-2019 HabanaLabs, Ltd. - * All Rights Reserved. - */ - -#include "habanalabs.h" - -#include - -#define SET_CLK_PKT_TIMEOUT 1000000 /* 1s */ -#define SET_PWR_PKT_TIMEOUT 1000000 /* 1s */ - -long hl_get_frequency(struct hl_device *hdev, u32 pll_index, bool curr) -{ - struct armcp_packet pkt; - long result; - int rc; - - memset(&pkt, 0, sizeof(pkt)); - - if (curr) - pkt.ctl = cpu_to_le32(ARMCP_PACKET_FREQUENCY_CURR_GET << - ARMCP_PKT_CTL_OPCODE_SHIFT); - else - pkt.ctl = cpu_to_le32(ARMCP_PACKET_FREQUENCY_GET << - ARMCP_PKT_CTL_OPCODE_SHIFT); - pkt.pll_index = cpu_to_le32(pll_index); - - rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), - SET_CLK_PKT_TIMEOUT, &result); - - if (rc) { - dev_err(hdev->dev, - "Failed to get frequency of PLL %d, error %d\n", - pll_index, rc); - result = rc; - } - - return result; -} - -void hl_set_frequency(struct hl_device *hdev, u32 pll_index, u64 freq) -{ - struct armcp_packet pkt; - int rc; - - memset(&pkt, 0, sizeof(pkt)); - - pkt.ctl = cpu_to_le32(ARMCP_PACKET_FREQUENCY_SET << - ARMCP_PKT_CTL_OPCODE_SHIFT); - pkt.pll_index = cpu_to_le32(pll_index); - pkt.value = cpu_to_le64(freq); - - rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), - SET_CLK_PKT_TIMEOUT, NULL); - - if (rc) - dev_err(hdev->dev, - "Failed to set frequency to PLL %d, error %d\n", - pll_index, rc); -} - -u64 hl_get_max_power(struct hl_device *hdev) -{ - struct armcp_packet pkt; - long result; - int rc; - - memset(&pkt, 0, sizeof(pkt)); - - pkt.ctl = cpu_to_le32(ARMCP_PACKET_MAX_POWER_GET << - ARMCP_PKT_CTL_OPCODE_SHIFT); - - rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), - SET_PWR_PKT_TIMEOUT, &result); - - if (rc) { - dev_err(hdev->dev, "Failed to get max power, error %d\n", rc); - result = rc; - } - - return result; -} - -void hl_set_max_power(struct hl_device *hdev, u64 value) -{ - struct armcp_packet pkt; - int rc; - - memset(&pkt, 0, sizeof(pkt)); - - pkt.ctl = cpu_to_le32(ARMCP_PACKET_MAX_POWER_SET << - ARMCP_PKT_CTL_OPCODE_SHIFT); - pkt.value = cpu_to_le64(value); - - rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), - SET_PWR_PKT_TIMEOUT, NULL); - - if (rc) - dev_err(hdev->dev, "Failed to set max power, error %d\n", rc); -} - -static ssize_t uboot_ver_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct hl_device *hdev = dev_get_drvdata(dev); - - return sprintf(buf, "%s\n", hdev->asic_prop.uboot_ver); -} - -static ssize_t armcp_kernel_ver_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct hl_device *hdev = dev_get_drvdata(dev); - - return sprintf(buf, "%s", hdev->asic_prop.armcp_info.kernel_version); -} - -static ssize_t armcp_ver_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct hl_device *hdev = dev_get_drvdata(dev); - - return sprintf(buf, "%s\n", hdev->asic_prop.armcp_info.armcp_version); -} - -static ssize_t cpld_ver_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct hl_device *hdev = dev_get_drvdata(dev); - - return sprintf(buf, "0x%08x\n", - hdev->asic_prop.armcp_info.cpld_version); -} - -static ssize_t infineon_ver_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct hl_device *hdev = dev_get_drvdata(dev); - - return sprintf(buf, "0x%04x\n", - hdev->asic_prop.armcp_info.infineon_version); -} - -static ssize_t fuse_ver_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct hl_device *hdev = dev_get_drvdata(dev); - - return sprintf(buf, "%s\n", hdev->asic_prop.armcp_info.fuse_version); -} - -static ssize_t thermal_ver_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct hl_device *hdev = dev_get_drvdata(dev); - - return sprintf(buf, "%s", hdev->asic_prop.armcp_info.thermal_version); -} - -static ssize_t preboot_btl_ver_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct hl_device *hdev = dev_get_drvdata(dev); - - return sprintf(buf, "%s\n", hdev->asic_prop.preboot_ver); -} - -static ssize_t soft_reset_store(struct device *dev, - struct device_attribute *attr, const char *buf, - size_t count) -{ - struct hl_device *hdev = dev_get_drvdata(dev); - long value; - int rc; - - rc = kstrtoul(buf, 0, &value); - - if (rc) { - count = -EINVAL; - goto out; - } - - if (!hdev->supports_soft_reset) { - dev_err(hdev->dev, "Device does not support soft-reset\n"); - goto out; - } - - dev_warn(hdev->dev, "Soft-Reset requested through sysfs\n"); - - hl_device_reset(hdev, false, false); - -out: - return count; -} - -static ssize_t hard_reset_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct hl_device *hdev = dev_get_drvdata(dev); - long value; - int rc; - - rc = kstrtoul(buf, 0, &value); - - if (rc) { - count = -EINVAL; - goto out; - } - - dev_warn(hdev->dev, "Hard-Reset requested through sysfs\n"); - - hl_device_reset(hdev, true, false); - -out: - return count; -} - -static ssize_t device_type_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct hl_device *hdev = dev_get_drvdata(dev); - char *str; - - switch (hdev->asic_type) { - case ASIC_GOYA: - str = "GOYA"; - break; - case ASIC_GAUDI: - str = "GAUDI"; - break; - default: - dev_err(hdev->dev, "Unrecognized ASIC type %d\n", - hdev->asic_type); - return -EINVAL; - } - - return sprintf(buf, "%s\n", str); -} - -static ssize_t pci_addr_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct hl_device *hdev = dev_get_drvdata(dev); - - return sprintf(buf, "%04x:%02x:%02x.%x\n", - pci_domain_nr(hdev->pdev->bus), - hdev->pdev->bus->number, - PCI_SLOT(hdev->pdev->devfn), - PCI_FUNC(hdev->pdev->devfn)); -} - -static ssize_t status_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct hl_device *hdev = dev_get_drvdata(dev); - char *str; - - if (atomic_read(&hdev->in_reset)) - str = "In reset"; - else if (hdev->disabled) - str = "Malfunction"; - else - str = "Operational"; - - return sprintf(buf, "%s\n", str); -} - -static ssize_t soft_reset_cnt_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct hl_device *hdev = dev_get_drvdata(dev); - - return sprintf(buf, "%d\n", hdev->soft_reset_cnt); -} - -static ssize_t hard_reset_cnt_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct hl_device *hdev = dev_get_drvdata(dev); - - return sprintf(buf, "%d\n", hdev->hard_reset_cnt); -} - -static ssize_t max_power_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct hl_device *hdev = dev_get_drvdata(dev); - long val; - - if (hl_device_disabled_or_in_reset(hdev)) - return -ENODEV; - - val = hl_get_max_power(hdev); - - return sprintf(buf, "%lu\n", val); -} - -static ssize_t max_power_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - struct hl_device *hdev = dev_get_drvdata(dev); - unsigned long value; - int rc; - - if (hl_device_disabled_or_in_reset(hdev)) { - count = -ENODEV; - goto out; - } - - rc = kstrtoul(buf, 0, &value); - - if (rc) { - count = -EINVAL; - goto out; - } - - hdev->max_power = value; - hl_set_max_power(hdev, value); - -out: - return count; -} - -static ssize_t eeprom_read_handler(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, char *buf, loff_t offset, - size_t max_size) -{ - struct device *dev = container_of(kobj, struct device, kobj); - struct hl_device *hdev = dev_get_drvdata(dev); - char *data; - int rc; - - if (!max_size) - return -EINVAL; - - data = kzalloc(max_size, GFP_KERNEL); - if (!data) - return -ENOMEM; - - rc = hdev->asic_funcs->get_eeprom_data(hdev, data, max_size); - if (rc) - goto out; - - memcpy(buf, data, max_size); - -out: - kfree(data); - - return max_size; -} - -static DEVICE_ATTR_RO(armcp_kernel_ver); -static DEVICE_ATTR_RO(armcp_ver); -static DEVICE_ATTR_RO(cpld_ver); -static DEVICE_ATTR_RO(device_type); -static DEVICE_ATTR_RO(fuse_ver); -static DEVICE_ATTR_WO(hard_reset); -static DEVICE_ATTR_RO(hard_reset_cnt); -static DEVICE_ATTR_RO(infineon_ver); -static DEVICE_ATTR_RW(max_power); -static DEVICE_ATTR_RO(pci_addr); -static DEVICE_ATTR_RO(preboot_btl_ver); -static DEVICE_ATTR_WO(soft_reset); -static DEVICE_ATTR_RO(soft_reset_cnt); -static DEVICE_ATTR_RO(status); -static DEVICE_ATTR_RO(thermal_ver); -static DEVICE_ATTR_RO(uboot_ver); - -static struct bin_attribute bin_attr_eeprom = { - .attr = {.name = "eeprom", .mode = (0444)}, - .size = PAGE_SIZE, - .read = eeprom_read_handler -}; - -static struct attribute *hl_dev_attrs[] = { - &dev_attr_armcp_kernel_ver.attr, - &dev_attr_armcp_ver.attr, - &dev_attr_cpld_ver.attr, - &dev_attr_device_type.attr, - &dev_attr_fuse_ver.attr, - &dev_attr_hard_reset.attr, - &dev_attr_hard_reset_cnt.attr, - &dev_attr_infineon_ver.attr, - &dev_attr_max_power.attr, - &dev_attr_pci_addr.attr, - &dev_attr_preboot_btl_ver.attr, - &dev_attr_soft_reset.attr, - &dev_attr_soft_reset_cnt.attr, - &dev_attr_status.attr, - &dev_attr_thermal_ver.attr, - &dev_attr_uboot_ver.attr, - NULL, -}; - -static struct bin_attribute *hl_dev_bin_attrs[] = { - &bin_attr_eeprom, - NULL -}; - -static struct attribute_group hl_dev_attr_group = { - .attrs = hl_dev_attrs, - .bin_attrs = hl_dev_bin_attrs, -}; - -static struct attribute_group hl_dev_clks_attr_group; - -static const struct attribute_group *hl_dev_attr_groups[] = { - &hl_dev_attr_group, - &hl_dev_clks_attr_group, - NULL, -}; - -int hl_sysfs_init(struct hl_device *hdev) -{ - int rc; - - if (hdev->asic_type == ASIC_GOYA) - hdev->pm_mng_profile = PM_AUTO; - else - hdev->pm_mng_profile = PM_MANUAL; - hdev->max_power = hdev->asic_prop.max_power_default; - - hdev->asic_funcs->add_device_attr(hdev, &hl_dev_clks_attr_group); - - rc = device_add_groups(hdev->dev, hl_dev_attr_groups); - if (rc) { - dev_err(hdev->dev, - "Failed to add groups to device, error %d\n", rc); - return rc; - } - - return 0; -} - -void hl_sysfs_fini(struct hl_device *hdev) -{ - device_remove_groups(hdev->dev, hl_dev_attr_groups); -} -- cgit v1.2.3-59-g8ed1b From eb8b293e794bbbafa9d615ea939982a19bf92867 Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Mon, 13 Jul 2020 13:11:33 +0300 Subject: habanalabs: update hl_boot_if.h from firmware Update the boot interface file from the latest version from firmware. Defines for secure boot were added. Signed-off-by: Oded Gabbay Reviewed-by: Omer Shpigelman --- drivers/misc/habanalabs/include/common/hl_boot_if.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/misc/habanalabs/include/common/hl_boot_if.h b/drivers/misc/habanalabs/include/common/hl_boot_if.h index c22d134e73af..bb67cafc6e00 100644 --- a/drivers/misc/habanalabs/include/common/hl_boot_if.h +++ b/drivers/misc/habanalabs/include/common/hl_boot_if.h @@ -44,6 +44,15 @@ * The NIC FW loading and initialization * failed. This means NICs are not usable. * + * CPU_BOOT_ERR0_SECURITY_NOT_RDY Chip security initialization has been + * started, but is not ready yet - chip + * cannot be accessed. + * + * CPU_BOOT_ERR0_SECURITY_FAIL Security related tasks have failed. + * The tasks are security init (root of + * trust), boot authentication (chain of + * trust), data packets authentication. + * * CPU_BOOT_ERR0_ENABLED Error registers enabled. * This is a main indication that the * running FW populates the error @@ -57,6 +66,8 @@ #define CPU_BOOT_ERR0_BMC_WAIT_SKIPPED (1 << 4) #define CPU_BOOT_ERR0_NIC_DATA_NOT_RDY (1 << 5) #define CPU_BOOT_ERR0_NIC_FW_FAIL (1 << 6) +#define CPU_BOOT_ERR0_SECURITY_NOT_RDY (1 << 7) +#define CPU_BOOT_ERR0_SECURITY_FAIL (1 << 8) #define CPU_BOOT_ERR0_ENABLED (1 << 31) enum cpu_boot_status { @@ -79,7 +90,10 @@ enum cpu_boot_status { CPU_BOOT_STATUS_BMC_WAITING_SKIPPED, /* deprecated - will be removed */ /* Last boot loader progress status, ready to receive commands */ CPU_BOOT_STATUS_READY_TO_BOOT = 15, + /* Internal Boot finished, ready for boot-fit */ CPU_BOOT_STATUS_WAITING_FOR_BOOT_FIT = 16, + /* Internal Security has been initialized, device can be accessed */ + CPU_BOOT_STATUS_SECURITY_READY = 17, }; enum kmd_msg { -- cgit v1.2.3-59-g8ed1b From a04b7cd97eef13a489ca44c979cf91e24cfa7b55 Mon Sep 17 00:00:00 2001 From: Ofir Bitton Date: Mon, 13 Jul 2020 13:36:55 +0300 Subject: habanalabs: create internal CB pool Create a device MMU-mapped internal command buffer pool, in order to allow the driver to allocate CBs for the signal/wait operations that are fetched by the queues when they are configured with the user's address space ID. We must pre-map this internal pool due to performance issues. This pool is needed for future ASIC support and it is currently unused in GOYA and GAUDI. Signed-off-by: Ofir Bitton Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/command_buffer.c | 82 ++++++++++++++-------- .../misc/habanalabs/common/command_submission.c | 13 ++-- drivers/misc/habanalabs/common/context.c | 8 +++ drivers/misc/habanalabs/common/habanalabs.h | 18 ++++- drivers/misc/habanalabs/gaudi/gaudi.c | 20 +++--- drivers/misc/habanalabs/goya/goya.c | 18 +++-- 6 files changed, 106 insertions(+), 53 deletions(-) diff --git a/drivers/misc/habanalabs/common/command_buffer.c b/drivers/misc/habanalabs/common/command_buffer.c index 02d13f71b1df..7c38c4f7f9c0 100644 --- a/drivers/misc/habanalabs/common/command_buffer.c +++ b/drivers/misc/habanalabs/common/command_buffer.c @@ -10,12 +10,18 @@ #include #include +#include static void cb_fini(struct hl_device *hdev, struct hl_cb *cb) { - hdev->asic_funcs->asic_dma_free_coherent(hdev, cb->size, - (void *) (uintptr_t) cb->kernel_address, - cb->bus_address); + if (cb->is_internal) + gen_pool_free(hdev->internal_cb_pool, + cb->kernel_address, cb->size); + else + hdev->asic_funcs->asic_dma_free_coherent(hdev, cb->size, + (void *) (uintptr_t) cb->kernel_address, + cb->bus_address); + kfree(cb); } @@ -44,9 +50,10 @@ static void cb_release(struct kref *ref) } static struct hl_cb *hl_cb_alloc(struct hl_device *hdev, u32 cb_size, - int ctx_id) + int ctx_id, bool internal_cb) { struct hl_cb *cb; + u32 cb_offset; void *p; /* @@ -65,13 +72,25 @@ static struct hl_cb *hl_cb_alloc(struct hl_device *hdev, u32 cb_size, if (!cb) return NULL; - if (ctx_id == HL_KERNEL_ASID_ID) + if (internal_cb) { + p = (void *) gen_pool_alloc(hdev->internal_cb_pool, cb_size); + if (!p) { + kfree(cb); + return NULL; + } + + cb_offset = p - hdev->internal_cb_pool_virt_addr; + cb->is_internal = true; + cb->bus_address = hdev->internal_cb_va_base + cb_offset; + } else if (ctx_id == HL_KERNEL_ASID_ID) { p = hdev->asic_funcs->asic_dma_alloc_coherent(hdev, cb_size, &cb->bus_address, GFP_ATOMIC); - else + } else { p = hdev->asic_funcs->asic_dma_alloc_coherent(hdev, cb_size, &cb->bus_address, GFP_USER | __GFP_ZERO); + } + if (!p) { dev_err(hdev->dev, "failed to allocate %d of dma memory for CB\n", @@ -87,7 +106,7 @@ static struct hl_cb *hl_cb_alloc(struct hl_device *hdev, u32 cb_size, } int hl_cb_create(struct hl_device *hdev, struct hl_cb_mgr *mgr, - u32 cb_size, u64 *handle, int ctx_id) + u32 cb_size, u64 *handle, int ctx_id, bool internal_cb) { struct hl_cb *cb; bool alloc_new_cb = true; @@ -112,28 +131,30 @@ int hl_cb_create(struct hl_device *hdev, struct hl_cb_mgr *mgr, goto out_err; } - /* Minimum allocation must be PAGE SIZE */ - if (cb_size < PAGE_SIZE) - cb_size = PAGE_SIZE; - - if (ctx_id == HL_KERNEL_ASID_ID && - cb_size <= hdev->asic_prop.cb_pool_cb_size) { - - spin_lock(&hdev->cb_pool_lock); - if (!list_empty(&hdev->cb_pool)) { - cb = list_first_entry(&hdev->cb_pool, typeof(*cb), - pool_list); - list_del(&cb->pool_list); - spin_unlock(&hdev->cb_pool_lock); - alloc_new_cb = false; - } else { - spin_unlock(&hdev->cb_pool_lock); - dev_dbg(hdev->dev, "CB pool is empty\n"); + if (!internal_cb) { + /* Minimum allocation must be PAGE SIZE */ + if (cb_size < PAGE_SIZE) + cb_size = PAGE_SIZE; + + if (ctx_id == HL_KERNEL_ASID_ID && + cb_size <= hdev->asic_prop.cb_pool_cb_size) { + + spin_lock(&hdev->cb_pool_lock); + if (!list_empty(&hdev->cb_pool)) { + cb = list_first_entry(&hdev->cb_pool, + typeof(*cb), pool_list); + list_del(&cb->pool_list); + spin_unlock(&hdev->cb_pool_lock); + alloc_new_cb = false; + } else { + spin_unlock(&hdev->cb_pool_lock); + dev_dbg(hdev->dev, "CB pool is empty\n"); + } } } if (alloc_new_cb) { - cb = hl_cb_alloc(hdev, cb_size, ctx_id); + cb = hl_cb_alloc(hdev, cb_size, ctx_id, internal_cb); if (!cb) { rc = -ENOMEM; goto out_err; @@ -229,8 +250,8 @@ int hl_cb_ioctl(struct hl_fpriv *hpriv, void *data) rc = -EINVAL; } else { rc = hl_cb_create(hdev, &hpriv->cb_mgr, - args->in.cb_size, &handle, - hpriv->ctx->asid); + args->in.cb_size, &handle, + hpriv->ctx->asid, false); } memset(args, 0, sizeof(*args)); @@ -398,14 +419,15 @@ void hl_cb_mgr_fini(struct hl_device *hdev, struct hl_cb_mgr *mgr) idr_destroy(&mgr->cb_handles); } -struct hl_cb *hl_cb_kernel_create(struct hl_device *hdev, u32 cb_size) +struct hl_cb *hl_cb_kernel_create(struct hl_device *hdev, u32 cb_size, + bool internal_cb) { u64 cb_handle; struct hl_cb *cb; int rc; rc = hl_cb_create(hdev, &hdev->kernel_cb_mgr, cb_size, &cb_handle, - HL_KERNEL_ASID_ID); + HL_KERNEL_ASID_ID, internal_cb); if (rc) { dev_err(hdev->dev, "Failed to allocate CB for the kernel driver %d\n", rc); @@ -437,7 +459,7 @@ int hl_cb_pool_init(struct hl_device *hdev) for (i = 0 ; i < hdev->asic_prop.cb_pool_cb_cnt ; i++) { cb = hl_cb_alloc(hdev, hdev->asic_prop.cb_pool_cb_size, - HL_KERNEL_ASID_ID); + HL_KERNEL_ASID_ID, false); if (cb) { cb->is_pool = true; list_add(&cb->pool_list, &hdev->cb_pool); diff --git a/drivers/misc/habanalabs/common/command_submission.c b/drivers/misc/habanalabs/common/command_submission.c index c605be89f764..e096532c0e48 100644 --- a/drivers/misc/habanalabs/common/command_submission.c +++ b/drivers/misc/habanalabs/common/command_submission.c @@ -919,7 +919,13 @@ static int cs_ioctl_signal_wait(struct hl_fpriv *hpriv, enum hl_cs_type cs_type, goto put_cs; } - cb = hl_cb_kernel_create(hdev, PAGE_SIZE); + if (cs->type == CS_TYPE_WAIT) + cb_size = hdev->asic_funcs->get_wait_cb_size(hdev); + else + cb_size = hdev->asic_funcs->get_signal_cb_size(hdev); + + cb = hl_cb_kernel_create(hdev, cb_size, + q_type == QUEUE_TYPE_HW && hdev->mmu_enable); if (!cb) { ctx->cs_counters.out_of_mem_drop_cnt++; kfree(job); @@ -927,11 +933,6 @@ static int cs_ioctl_signal_wait(struct hl_fpriv *hpriv, enum hl_cs_type cs_type, goto put_cs; } - if (cs->type == CS_TYPE_WAIT) - cb_size = hdev->asic_funcs->get_wait_cb_size(hdev); - else - cb_size = hdev->asic_funcs->get_signal_cb_size(hdev); - job->id = 0; job->cs = cs; job->user_cb = cb; diff --git a/drivers/misc/habanalabs/common/context.c b/drivers/misc/habanalabs/common/context.c index 1e3e5b19ecd9..b75a20364fad 100644 --- a/drivers/misc/habanalabs/common/context.c +++ b/drivers/misc/habanalabs/common/context.c @@ -153,10 +153,18 @@ int hl_ctx_init(struct hl_device *hdev, struct hl_ctx *ctx, bool is_kernel_ctx) rc = -ENOMEM; goto mem_ctx_err; } + + rc = hdev->asic_funcs->ctx_init(ctx); + if (rc) { + dev_err(hdev->dev, "ctx_init failed\n"); + goto ctx_init_err; + } } return 0; +ctx_init_err: + hl_vm_ctx_fini(ctx); mem_ctx_err: if (ctx->asid != HL_KERNEL_ASID_ID) hl_asid_free(hdev, ctx->asid); diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index 82532f1f94cb..bf9abfa47b7a 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -392,6 +392,7 @@ struct hl_cb_mgr { * @ctx_id: holds the ID of the owner's context. * @mmap: true if the CB is currently mmaped to user. * @is_pool: true if CB was acquired from the pool, false otherwise. + * @is_internal: internaly allocated */ struct hl_cb { struct kref refcount; @@ -408,6 +409,7 @@ struct hl_cb { u32 ctx_id; u8 mmap; u8 is_pool; + u8 is_internal; }; @@ -643,6 +645,7 @@ enum div_select_defs { * @rreg: Read a register. Needed for simulator support. * @wreg: Write a register. Needed for simulator support. * @halt_coresight: stop the ETF and ETR traces. + * @ctx_init: context dependent initialization. * @get_clk_rate: Retrieve the ASIC current and maximum clock rate in MHz * @get_queue_id_for_cq: Get the H/W queue id related to the given CQ index. * @read_device_fw_version: read the device's firmware versions that are @@ -745,6 +748,7 @@ struct hl_asic_funcs { u32 (*rreg)(struct hl_device *hdev, u32 reg); void (*wreg)(struct hl_device *hdev, u32 reg, u32 val); void (*halt_coresight)(struct hl_device *hdev); + int (*ctx_init)(struct hl_ctx *ctx); int (*get_clk_rate)(struct hl_device *hdev, u32 *cur_clk, u32 *max_clk); u32 (*get_queue_id_for_cq)(struct hl_device *hdev, u32 cq_idx); void (*read_device_fw_version)(struct hl_device *hdev, @@ -1432,6 +1436,10 @@ struct hl_device_idle_busy_ts { * @hl_debugfs: device's debugfs manager. * @cb_pool: list of preallocated CBs. * @cb_pool_lock: protects the CB pool. + * @internal_cb_pool_virt_addr: internal command buffer pool virtual address. + * @internal_cb_pool_dma_addr: internal command buffer pool dma address. + * @internal_cb_pool: internal command buffer memory pool. + * @internal_cb_va_base: internal cb pool mmu virtual address base * @fpriv_list: list of file private data structures. Each structure is created * when a user opens the device * @fpriv_list_lock: protects the fpriv_list @@ -1531,6 +1539,11 @@ struct hl_device { struct list_head cb_pool; spinlock_t cb_pool_lock; + void *internal_cb_pool_virt_addr; + dma_addr_t internal_cb_pool_dma_addr; + struct gen_pool *internal_cb_pool; + u64 internal_cb_va_base; + struct list_head fpriv_list; struct mutex fpriv_list_lock; @@ -1741,7 +1754,7 @@ int hl_hwmon_init(struct hl_device *hdev); void hl_hwmon_fini(struct hl_device *hdev); int hl_cb_create(struct hl_device *hdev, struct hl_cb_mgr *mgr, u32 cb_size, - u64 *handle, int ctx_id); + u64 *handle, int ctx_id, bool internal_cb); int hl_cb_destroy(struct hl_device *hdev, struct hl_cb_mgr *mgr, u64 cb_handle); int hl_cb_mmap(struct hl_fpriv *hpriv, struct vm_area_struct *vma); struct hl_cb *hl_cb_get(struct hl_device *hdev, struct hl_cb_mgr *mgr, @@ -1749,7 +1762,8 @@ struct hl_cb *hl_cb_get(struct hl_device *hdev, struct hl_cb_mgr *mgr, void hl_cb_put(struct hl_cb *cb); void hl_cb_mgr_init(struct hl_cb_mgr *mgr); void hl_cb_mgr_fini(struct hl_device *hdev, struct hl_cb_mgr *mgr); -struct hl_cb *hl_cb_kernel_create(struct hl_device *hdev, u32 cb_size); +struct hl_cb *hl_cb_kernel_create(struct hl_device *hdev, u32 cb_size, + bool internal_cb); int hl_cb_pool_init(struct hl_device *hdev); int hl_cb_pool_fini(struct hl_device *hdev); diff --git a/drivers/misc/habanalabs/gaudi/gaudi.c b/drivers/misc/habanalabs/gaudi/gaudi.c index 57b2b9392cb2..86cfaf73ad74 100644 --- a/drivers/misc/habanalabs/gaudi/gaudi.c +++ b/drivers/misc/habanalabs/gaudi/gaudi.c @@ -635,7 +635,7 @@ static int _gaudi_init_tpc_mem(struct hl_device *hdev, u8 tpc_id; int rc; - cb = hl_cb_kernel_create(hdev, PAGE_SIZE); + cb = hl_cb_kernel_create(hdev, PAGE_SIZE, false); if (!cb) return -EFAULT; @@ -4048,9 +4048,8 @@ static int gaudi_parse_cb_mmu(struct hl_device *hdev, parser->patched_cb_size = parser->user_cb_size + sizeof(struct packet_msg_prot) * 2; - rc = hl_cb_create(hdev, &hdev->kernel_cb_mgr, - parser->patched_cb_size, - &patched_cb_handle, HL_KERNEL_ASID_ID); + rc = hl_cb_create(hdev, &hdev->kernel_cb_mgr, parser->patched_cb_size, + &patched_cb_handle, HL_KERNEL_ASID_ID, false); if (rc) { dev_err(hdev->dev, @@ -4122,9 +4121,8 @@ static int gaudi_parse_cb_no_mmu(struct hl_device *hdev, if (rc) goto free_userptr; - rc = hl_cb_create(hdev, &hdev->kernel_cb_mgr, - parser->patched_cb_size, - &patched_cb_handle, HL_KERNEL_ASID_ID); + rc = hl_cb_create(hdev, &hdev->kernel_cb_mgr, parser->patched_cb_size, + &patched_cb_handle, HL_KERNEL_ASID_ID, false); if (rc) { dev_err(hdev->dev, "Failed to allocate patched CB for DMA CS %d\n", rc); @@ -4257,7 +4255,7 @@ static int gaudi_memset_device_memory(struct hl_device *hdev, u64 addr, struct hl_cb *cb; int rc; - cb = hl_cb_kernel_create(hdev, PAGE_SIZE); + cb = hl_cb_kernel_create(hdev, PAGE_SIZE, false); if (!cb) return -EFAULT; @@ -6229,6 +6227,11 @@ static enum hl_device_hw_state gaudi_get_hw_state(struct hl_device *hdev) return RREG32(mmHW_STATE); } +int gaudi_ctx_init(struct hl_ctx *ctx) +{ + return 0; +} + static u32 gaudi_get_queue_id_for_cq(struct hl_device *hdev, u32 cq_idx) { return gaudi_cq_assignment[cq_idx]; @@ -6532,6 +6535,7 @@ static const struct hl_asic_funcs gaudi_funcs = { .rreg = hl_rreg, .wreg = hl_wreg, .halt_coresight = gaudi_halt_coresight, + .ctx_init = gaudi_ctx_init, .get_clk_rate = gaudi_get_clk_rate, .get_queue_id_for_cq = gaudi_get_queue_id_for_cq, .read_device_fw_version = gaudi_read_device_fw_version, diff --git a/drivers/misc/habanalabs/goya/goya.c b/drivers/misc/habanalabs/goya/goya.c index 2b0937d950c1..4473ded313d6 100644 --- a/drivers/misc/habanalabs/goya/goya.c +++ b/drivers/misc/habanalabs/goya/goya.c @@ -3771,9 +3771,8 @@ static int goya_parse_cb_mmu(struct hl_device *hdev, parser->patched_cb_size = parser->user_cb_size + sizeof(struct packet_msg_prot) * 2; - rc = hl_cb_create(hdev, &hdev->kernel_cb_mgr, - parser->patched_cb_size, - &patched_cb_handle, HL_KERNEL_ASID_ID); + rc = hl_cb_create(hdev, &hdev->kernel_cb_mgr, parser->patched_cb_size, + &patched_cb_handle, HL_KERNEL_ASID_ID, false); if (rc) { dev_err(hdev->dev, @@ -3845,9 +3844,8 @@ static int goya_parse_cb_no_mmu(struct hl_device *hdev, if (rc) goto free_userptr; - rc = hl_cb_create(hdev, &hdev->kernel_cb_mgr, - parser->patched_cb_size, - &patched_cb_handle, HL_KERNEL_ASID_ID); + rc = hl_cb_create(hdev, &hdev->kernel_cb_mgr, parser->patched_cb_size, + &patched_cb_handle, HL_KERNEL_ASID_ID, false); if (rc) { dev_err(hdev->dev, "Failed to allocate patched CB for DMA CS %d\n", rc); @@ -4693,7 +4691,7 @@ static int goya_memset_device_memory(struct hl_device *hdev, u64 addr, u64 size, lin_dma_pkts_cnt = DIV_ROUND_UP_ULL(size, SZ_2G); cb_size = lin_dma_pkts_cnt * sizeof(struct packet_lin_dma) + sizeof(struct packet_msg_prot); - cb = hl_cb_kernel_create(hdev, cb_size); + cb = hl_cb_kernel_create(hdev, cb_size, false); if (!cb) return -ENOMEM; @@ -5223,6 +5221,11 @@ static enum hl_device_hw_state goya_get_hw_state(struct hl_device *hdev) return RREG32(mmHW_STATE); } +int goya_ctx_init(struct hl_ctx *ctx) +{ + return 0; +} + u32 goya_get_queue_id_for_cq(struct hl_device *hdev, u32 cq_idx) { return cq_idx; @@ -5336,6 +5339,7 @@ static const struct hl_asic_funcs goya_funcs = { .rreg = hl_rreg, .wreg = hl_wreg, .halt_coresight = goya_halt_coresight, + .ctx_init = goya_ctx_init, .get_clk_rate = goya_get_clk_rate, .get_queue_id_for_cq = goya_get_queue_id_for_cq, .read_device_fw_version = goya_read_device_fw_version, -- cgit v1.2.3-59-g8ed1b From 8df8cb1efc1962356c97656839ea9c41e1ed4ba9 Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Wed, 15 Jul 2020 21:59:32 +0300 Subject: habanalabs: enable device before hw_init() Device is now enabled before the hw_init() because part of the initialization requires communication with the device firmware to get information that is required for the initialization itself Signed-off-by: Oded Gabbay Reviewed-by: Tomer Tayar --- drivers/misc/habanalabs/common/device.c | 16 ++++++++++++---- drivers/misc/habanalabs/common/sysfs.c | 3 +++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/drivers/misc/habanalabs/common/device.c b/drivers/misc/habanalabs/common/device.c index 84800efec10d..9919ff121067 100644 --- a/drivers/misc/habanalabs/common/device.c +++ b/drivers/misc/habanalabs/common/device.c @@ -1033,6 +1033,12 @@ again: } } + /* Device is now enabled as part of the initialization requires + * communication with the device firmware to get information that + * is required for the initialization itself + */ + hdev->disabled = false; + rc = hdev->asic_funcs->hw_init(hdev); if (rc) { dev_err(hdev->dev, @@ -1040,8 +1046,6 @@ again: goto out_err; } - hdev->disabled = false; - /* Check that the communication with the device is working */ rc = hdev->asic_funcs->test_queues(hdev); if (rc) { @@ -1261,6 +1265,12 @@ int hl_device_init(struct hl_device *hdev, struct class *hclass) */ add_cdev_sysfs_on_err = true; + /* Device is now enabled as part of the initialization requires + * communication with the device firmware to get information that + * is required for the initialization itself + */ + hdev->disabled = false; + rc = hdev->asic_funcs->hw_init(hdev); if (rc) { dev_err(hdev->dev, "failed to initialize the H/W\n"); @@ -1268,8 +1278,6 @@ int hl_device_init(struct hl_device *hdev, struct class *hclass) goto out_disabled; } - hdev->disabled = false; - /* Check that the communication with the device is working */ rc = hdev->asic_funcs->test_queues(hdev); if (rc) { diff --git a/drivers/misc/habanalabs/common/sysfs.c b/drivers/misc/habanalabs/common/sysfs.c index 5d78d5e1c782..c4e7c682d584 100644 --- a/drivers/misc/habanalabs/common/sysfs.c +++ b/drivers/misc/habanalabs/common/sysfs.c @@ -334,6 +334,9 @@ static ssize_t eeprom_read_handler(struct file *filp, struct kobject *kobj, char *data; int rc; + if (hl_device_disabled_or_in_reset(hdev)) + return -ENODEV; + if (!max_size) return -EINVAL; -- cgit v1.2.3-59-g8ed1b From 644883ef1aa53af1efecf3f84df9951c448b876b Mon Sep 17 00:00:00 2001 From: Tomer Tayar Date: Sun, 19 Jul 2020 11:00:03 +0300 Subject: habanalabs: use no flags on MMU cache invalidation gaudi_mmu_invalidate_cache() doesn't use the flags parameter, and thus it can be set to 0 when the function is called in the gaudi only files. Signed-off-by: Tomer Tayar Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/gaudi/gaudi.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/misc/habanalabs/gaudi/gaudi.c b/drivers/misc/habanalabs/gaudi/gaudi.c index 86cfaf73ad74..4a1a52608fc0 100644 --- a/drivers/misc/habanalabs/gaudi/gaudi.c +++ b/drivers/misc/habanalabs/gaudi/gaudi.c @@ -2646,8 +2646,7 @@ static int gaudi_mmu_init(struct hl_device *hdev) WREG32(mmSTLB_CACHE_INV_BASE_39_8, MMU_CACHE_MNG_ADDR >> 8); WREG32(mmSTLB_CACHE_INV_BASE_49_40, MMU_CACHE_MNG_ADDR >> 40); - hdev->asic_funcs->mmu_invalidate_cache(hdev, true, - VM_TYPE_USERPTR | VM_TYPE_PHYS_PACK); + hdev->asic_funcs->mmu_invalidate_cache(hdev, true, 0); WREG32(mmMMU_UP_MMU_ENABLE, 1); WREG32(mmMMU_UP_SPI_MASK, 0xF); -- cgit v1.2.3-59-g8ed1b From 94f8be9eb065412cf069efd45053d33e8911fa9e Mon Sep 17 00:00:00 2001 From: Tomer Tayar Date: Thu, 23 Jul 2020 09:17:57 +0300 Subject: habanalabs: Fix memory leak in error flow of context initialization Add a missing free of the cs_pending array in the error flow of context initialization. Fixes: c16d45f42b64 ("habanalabs: Use pending CS amount per ASIC") Signed-off-by: Tomer Tayar Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/context.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/drivers/misc/habanalabs/common/context.c b/drivers/misc/habanalabs/common/context.c index b75a20364fad..3e375958e73b 100644 --- a/drivers/misc/habanalabs/common/context.c +++ b/drivers/misc/habanalabs/common/context.c @@ -138,36 +138,38 @@ int hl_ctx_init(struct hl_device *hdev, struct hl_ctx *ctx, bool is_kernel_ctx) rc = hl_mmu_ctx_init(ctx); if (rc) { dev_err(hdev->dev, "Failed to init mmu ctx module\n"); - goto mem_ctx_err; + goto err_free_cs_pending; } } else { ctx->asid = hl_asid_alloc(hdev); if (!ctx->asid) { dev_err(hdev->dev, "No free ASID, failed to create context\n"); - return -ENOMEM; + rc = -ENOMEM; + goto err_free_cs_pending; } rc = hl_vm_ctx_init(ctx); if (rc) { dev_err(hdev->dev, "Failed to init mem ctx module\n"); rc = -ENOMEM; - goto mem_ctx_err; + goto err_asid_free; } rc = hdev->asic_funcs->ctx_init(ctx); if (rc) { dev_err(hdev->dev, "ctx_init failed\n"); - goto ctx_init_err; + goto err_vm_ctx_fini; } } return 0; -ctx_init_err: +err_vm_ctx_fini: hl_vm_ctx_fini(ctx); -mem_ctx_err: - if (ctx->asid != HL_KERNEL_ASID_ID) - hl_asid_free(hdev, ctx->asid); +err_asid_free: + hl_asid_free(hdev, ctx->asid); +err_free_cs_pending: + kfree(ctx->cs_pending); return rc; } -- cgit v1.2.3-59-g8ed1b From 7b16a15524d5db449cf5e529602610cdf04eddab Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 28 Jul 2020 19:18:51 +0200 Subject: habanalabs: fix up absolute include instructions There's no need to try to be cute with the include file locations in the Makefile, so just specify exactly where the files are. Bonus is this fixes the problem of building with O= as well as trying to just build the subdirectory alone. Reported-by: Stephen Rothwell Cc: Oded Gabbay Cc: Omer Shpigelman Cc: Tomer Tayar Cc: Moti Haimovski Cc: Ofir Bitton Cc: Ben Segal Cc: Christine Gharzuzi Cc: Pawel Piskorski Link: https://lore.kernel.org/r/20200728171851.55842-1-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/habanalabs/common/Makefile | 2 -- drivers/misc/habanalabs/common/debugfs.c | 2 +- drivers/misc/habanalabs/common/firmware_if.c | 2 +- drivers/misc/habanalabs/common/habanalabs.h | 4 ++-- drivers/misc/habanalabs/common/memory.c | 2 +- drivers/misc/habanalabs/common/mmu.c | 2 +- drivers/misc/habanalabs/common/pci.c | 2 +- drivers/misc/habanalabs/gaudi/Makefile | 2 -- drivers/misc/habanalabs/gaudi/gaudi.c | 12 ++++++------ drivers/misc/habanalabs/gaudi/gaudiP.h | 10 +++++----- drivers/misc/habanalabs/gaudi/gaudi_coresight.c | 6 +++--- drivers/misc/habanalabs/gaudi/gaudi_hwmgr.c | 2 +- drivers/misc/habanalabs/gaudi/gaudi_security.c | 2 +- drivers/misc/habanalabs/goya/Makefile | 2 -- drivers/misc/habanalabs/goya/goya.c | 8 ++++---- drivers/misc/habanalabs/goya/goyaP.h | 12 ++++++------ drivers/misc/habanalabs/goya/goya_coresight.c | 6 +++--- drivers/misc/habanalabs/goya/goya_security.c | 2 +- 18 files changed, 37 insertions(+), 43 deletions(-) diff --git a/drivers/misc/habanalabs/common/Makefile b/drivers/misc/habanalabs/common/Makefile index 97d03b5c8683..b984bfa4face 100644 --- a/drivers/misc/habanalabs/common/Makefile +++ b/drivers/misc/habanalabs/common/Makefile @@ -1,6 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only -subdir-ccflags-y += -I$(src)/common - HL_COMMON_FILES := common/habanalabs_drv.o common/device.o common/context.o \ common/asid.o common/habanalabs_ioctl.o \ common/command_buffer.o common/hw_queue.o common/irq.o \ diff --git a/drivers/misc/habanalabs/common/debugfs.c b/drivers/misc/habanalabs/common/debugfs.c index 0bc036e01ee8..c50c6fc9e905 100644 --- a/drivers/misc/habanalabs/common/debugfs.c +++ b/drivers/misc/habanalabs/common/debugfs.c @@ -6,7 +6,7 @@ */ #include "habanalabs.h" -#include "include/hw_ip/mmu/mmu_general.h" +#include "../include/hw_ip/mmu/mmu_general.h" #include #include diff --git a/drivers/misc/habanalabs/common/firmware_if.c b/drivers/misc/habanalabs/common/firmware_if.c index 5981dbd8c6df..f70302cdab1b 100644 --- a/drivers/misc/habanalabs/common/firmware_if.c +++ b/drivers/misc/habanalabs/common/firmware_if.c @@ -6,7 +6,7 @@ */ #include "habanalabs.h" -#include "include/common/hl_boot_if.h" +#include "../include/common/hl_boot_if.h" #include #include diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index eb42aa5476a9..018d9d67e8e6 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -8,8 +8,8 @@ #ifndef HABANALABSP_H_ #define HABANALABSP_H_ -#include "include/common/armcp_if.h" -#include "include/common/qman_if.h" +#include "../include/common/armcp_if.h" +#include "../include/common/qman_if.h" #include #include diff --git a/drivers/misc/habanalabs/common/memory.c b/drivers/misc/habanalabs/common/memory.c index e4e1693e5c6c..dce9273e557a 100644 --- a/drivers/misc/habanalabs/common/memory.c +++ b/drivers/misc/habanalabs/common/memory.c @@ -7,7 +7,7 @@ #include #include "habanalabs.h" -#include "include/hw_ip/mmu/mmu_general.h" +#include "../include/hw_ip/mmu/mmu_general.h" #include #include diff --git a/drivers/misc/habanalabs/common/mmu.c b/drivers/misc/habanalabs/common/mmu.c index 04303950e630..edcc11d5eaf1 100644 --- a/drivers/misc/habanalabs/common/mmu.c +++ b/drivers/misc/habanalabs/common/mmu.c @@ -6,7 +6,7 @@ */ #include "habanalabs.h" -#include "include/hw_ip/mmu/mmu_general.h" +#include "../include/hw_ip/mmu/mmu_general.h" #include #include diff --git a/drivers/misc/habanalabs/common/pci.c b/drivers/misc/habanalabs/common/pci.c index 1791f6623c69..7bd3737571f3 100644 --- a/drivers/misc/habanalabs/common/pci.c +++ b/drivers/misc/habanalabs/common/pci.c @@ -6,7 +6,7 @@ */ #include "habanalabs.h" -#include "include/hw_ip/pci/pci_general.h" +#include "../include/hw_ip/pci/pci_general.h" #include #include diff --git a/drivers/misc/habanalabs/gaudi/Makefile b/drivers/misc/habanalabs/gaudi/Makefile index 75104ae74e2b..c9f4703cff24 100644 --- a/drivers/misc/habanalabs/gaudi/Makefile +++ b/drivers/misc/habanalabs/gaudi/Makefile @@ -1,5 +1,3 @@ # SPDX-License-Identifier: GPL-2.0-only -subdir-ccflags-y += -I$(src)/common - HL_GAUDI_FILES := gaudi/gaudi.o gaudi/gaudi_hwmgr.o gaudi/gaudi_security.o \ gaudi/gaudi_coresight.o diff --git a/drivers/misc/habanalabs/gaudi/gaudi.c b/drivers/misc/habanalabs/gaudi/gaudi.c index 78fbff646f99..d4b3b995f69d 100644 --- a/drivers/misc/habanalabs/gaudi/gaudi.c +++ b/drivers/misc/habanalabs/gaudi/gaudi.c @@ -6,12 +6,12 @@ */ #include "gaudiP.h" -#include "include/hw_ip/mmu/mmu_general.h" -#include "include/hw_ip/mmu/mmu_v1_1.h" -#include "include/gaudi/gaudi_masks.h" -#include "include/gaudi/gaudi_fw_if.h" -#include "include/gaudi/gaudi_reg_map.h" -#include "include/gaudi/gaudi_async_ids_map_extended.h" +#include "../include/hw_ip/mmu/mmu_general.h" +#include "../include/hw_ip/mmu/mmu_v1_1.h" +#include "../include/gaudi/gaudi_masks.h" +#include "../include/gaudi/gaudi_fw_if.h" +#include "../include/gaudi/gaudi_reg_map.h" +#include "../include/gaudi/gaudi_async_ids_map_extended.h" #include #include diff --git a/drivers/misc/habanalabs/gaudi/gaudiP.h b/drivers/misc/habanalabs/gaudi/gaudiP.h index a94ab6a180f0..5dc99f6f0296 100644 --- a/drivers/misc/habanalabs/gaudi/gaudiP.h +++ b/drivers/misc/habanalabs/gaudi/gaudiP.h @@ -9,11 +9,11 @@ #define GAUDIP_H_ #include -#include "habanalabs.h" -#include "include/common/hl_boot_if.h" -#include "include/gaudi/gaudi_packets.h" -#include "include/gaudi/gaudi.h" -#include "include/gaudi/gaudi_async_events.h" +#include "../common/habanalabs.h" +#include "../include/common/hl_boot_if.h" +#include "../include/gaudi/gaudi_packets.h" +#include "../include/gaudi/gaudi.h" +#include "../include/gaudi/gaudi_async_events.h" #define NUMBER_OF_EXT_HW_QUEUES 12 #define NUMBER_OF_CMPLT_QUEUES NUMBER_OF_EXT_HW_QUEUES diff --git a/drivers/misc/habanalabs/gaudi/gaudi_coresight.c b/drivers/misc/habanalabs/gaudi/gaudi_coresight.c index c32322cb1728..5673ee49819e 100644 --- a/drivers/misc/habanalabs/gaudi/gaudi_coresight.c +++ b/drivers/misc/habanalabs/gaudi/gaudi_coresight.c @@ -6,9 +6,9 @@ */ #include "gaudiP.h" -#include "include/gaudi/gaudi_coresight.h" -#include "include/gaudi/asic_reg/gaudi_regs.h" -#include "include/gaudi/gaudi_masks.h" +#include "../include/gaudi/gaudi_coresight.h" +#include "../include/gaudi/asic_reg/gaudi_regs.h" +#include "../include/gaudi/gaudi_masks.h" #include #include diff --git a/drivers/misc/habanalabs/gaudi/gaudi_hwmgr.c b/drivers/misc/habanalabs/gaudi/gaudi_hwmgr.c index 6dd2c2a1cd70..1076b4932ce2 100644 --- a/drivers/misc/habanalabs/gaudi/gaudi_hwmgr.c +++ b/drivers/misc/habanalabs/gaudi/gaudi_hwmgr.c @@ -6,7 +6,7 @@ */ #include "gaudiP.h" -#include "include/gaudi/gaudi_fw_if.h" +#include "../include/gaudi/gaudi_fw_if.h" void gaudi_set_pll_profile(struct hl_device *hdev, enum hl_pll_frequency freq) { diff --git a/drivers/misc/habanalabs/gaudi/gaudi_security.c b/drivers/misc/habanalabs/gaudi/gaudi_security.c index abdd5ed8f2cf..8d5d6ddee6ed 100644 --- a/drivers/misc/habanalabs/gaudi/gaudi_security.c +++ b/drivers/misc/habanalabs/gaudi/gaudi_security.c @@ -6,7 +6,7 @@ */ #include "gaudiP.h" -#include "include/gaudi/asic_reg/gaudi_regs.h" +#include "../include/gaudi/asic_reg/gaudi_regs.h" #define GAUDI_NUMBER_OF_RR_REGS 24 #define GAUDI_NUMBER_OF_LBW_RANGES 12 diff --git a/drivers/misc/habanalabs/goya/Makefile b/drivers/misc/habanalabs/goya/Makefile index bd769083628e..b3f3b7b96683 100644 --- a/drivers/misc/habanalabs/goya/Makefile +++ b/drivers/misc/habanalabs/goya/Makefile @@ -1,5 +1,3 @@ # SPDX-License-Identifier: GPL-2.0-only -subdir-ccflags-y += -I$(src) - HL_GOYA_FILES := goya/goya.o goya/goya_security.o goya/goya_hwmgr.o \ goya/goya_coresight.o diff --git a/drivers/misc/habanalabs/goya/goya.c b/drivers/misc/habanalabs/goya/goya.c index a4dfea7556da..dedcd2211fb7 100644 --- a/drivers/misc/habanalabs/goya/goya.c +++ b/drivers/misc/habanalabs/goya/goya.c @@ -6,10 +6,10 @@ */ #include "goyaP.h" -#include "include/hw_ip/mmu/mmu_general.h" -#include "include/hw_ip/mmu/mmu_v1_0.h" -#include "include/goya/asic_reg/goya_masks.h" -#include "include/goya/goya_reg_map.h" +#include "../include/hw_ip/mmu/mmu_general.h" +#include "../include/hw_ip/mmu/mmu_v1_0.h" +#include "../include/goya/asic_reg/goya_masks.h" +#include "../include/goya/goya_reg_map.h" #include #include diff --git a/drivers/misc/habanalabs/goya/goyaP.h b/drivers/misc/habanalabs/goya/goyaP.h index 9e674cf39fd9..bb7474ee9784 100644 --- a/drivers/misc/habanalabs/goya/goyaP.h +++ b/drivers/misc/habanalabs/goya/goyaP.h @@ -9,12 +9,12 @@ #define GOYAP_H_ #include -#include "habanalabs.h" -#include "include/common/hl_boot_if.h" -#include "include/goya/goya_packets.h" -#include "include/goya/goya.h" -#include "include/goya/goya_async_events.h" -#include "include/goya/goya_fw_if.h" +#include "../common/habanalabs.h" +#include "../include/common/hl_boot_if.h" +#include "../include/goya/goya_packets.h" +#include "../include/goya/goya.h" +#include "../include/goya/goya_async_events.h" +#include "../include/goya/goya_fw_if.h" #define NUMBER_OF_CMPLT_QUEUES 5 #define NUMBER_OF_EXT_HW_QUEUES 5 diff --git a/drivers/misc/habanalabs/goya/goya_coresight.c b/drivers/misc/habanalabs/goya/goya_coresight.c index 18e12e9d284b..b03912483de0 100644 --- a/drivers/misc/habanalabs/goya/goya_coresight.c +++ b/drivers/misc/habanalabs/goya/goya_coresight.c @@ -6,9 +6,9 @@ */ #include "goyaP.h" -#include "include/goya/goya_coresight.h" -#include "include/goya/asic_reg/goya_regs.h" -#include "include/goya/asic_reg/goya_masks.h" +#include "../include/goya/goya_coresight.h" +#include "../include/goya/asic_reg/goya_regs.h" +#include "../include/goya/asic_reg/goya_masks.h" #include diff --git a/drivers/misc/habanalabs/goya/goya_security.c b/drivers/misc/habanalabs/goya/goya_security.c index de8297001fea..14701836f92b 100644 --- a/drivers/misc/habanalabs/goya/goya_security.c +++ b/drivers/misc/habanalabs/goya/goya_security.c @@ -6,7 +6,7 @@ */ #include "goyaP.h" -#include "include/goya/asic_reg/goya_regs.h" +#include "../include/goya/asic_reg/goya_regs.h" /* * goya_set_block_as_protected - set the given block as protected -- cgit v1.2.3-59-g8ed1b From bb34bf798ce7d53cf68a801a3cd96b47874ff867 Mon Sep 17 00:00:00 2001 From: kernel test robot Date: Wed, 29 Jul 2020 08:03:13 +0800 Subject: habanalabs: goya_ctx_init() can be static Signed-off-by: kernel test robot Link: https://lore.kernel.org/r/20200729000313.GA14680@e442e3f624c4 Signed-off-by: Greg Kroah-Hartman --- drivers/misc/habanalabs/gaudi/gaudi.c | 2 +- drivers/misc/habanalabs/goya/goya.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/misc/habanalabs/gaudi/gaudi.c b/drivers/misc/habanalabs/gaudi/gaudi.c index d4b3b995f69d..00a0a7238d81 100644 --- a/drivers/misc/habanalabs/gaudi/gaudi.c +++ b/drivers/misc/habanalabs/gaudi/gaudi.c @@ -6272,7 +6272,7 @@ static enum hl_device_hw_state gaudi_get_hw_state(struct hl_device *hdev) return RREG32(mmHW_STATE); } -int gaudi_ctx_init(struct hl_ctx *ctx) +static int gaudi_ctx_init(struct hl_ctx *ctx) { return 0; } diff --git a/drivers/misc/habanalabs/goya/goya.c b/drivers/misc/habanalabs/goya/goya.c index dedcd2211fb7..85030759b2af 100644 --- a/drivers/misc/habanalabs/goya/goya.c +++ b/drivers/misc/habanalabs/goya/goya.c @@ -5225,7 +5225,7 @@ static enum hl_device_hw_state goya_get_hw_state(struct hl_device *hdev) return RREG32(mmHW_STATE); } -int goya_ctx_init(struct hl_ctx *ctx) +static int goya_ctx_init(struct hl_ctx *ctx) { return 0; } -- cgit v1.2.3-59-g8ed1b From 9bb01fe1b685d9521b8a0aa59412f91f9a17a030 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Wed, 29 Jul 2020 14:05:40 +0300 Subject: MAINTAINERS: Fix maintainer entry for mei driver mei driver has sub modules, those are not listed via scripts/get_maintainer.pl when using asterisk: drivers/misc/mei/* The correct notation is: drivers/misc/mei/ Cc: Joe Perches Signed-off-by: Tomas Winkler Reviewed-by: Gustavo A. R. Silva Link: https://lore.kernel.org/r/20200729110540.3205585-1-tomas.winkler@intel.com Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 86994c35d56e..dbe6a71eb6f2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8789,7 +8789,7 @@ M: Tomas Winkler L: linux-kernel@vger.kernel.org S: Supported F: Documentation/driver-api/mei/* -F: drivers/misc/mei/* +F: drivers/misc/mei/ F: drivers/watchdog/mei_wdt.c F: include/linux/mei_cl_bus.h F: include/uapi/linux/mei.h -- cgit v1.2.3-59-g8ed1b From 2f79d3d1f7f0885d574811f80c6e0473ab8ef5ab Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Tue, 28 Jul 2020 22:22:42 +0300 Subject: mei: add device kind to sysfs Some of the mei device heads are not generic and have a specific purpose, we need to announce it to the user space so it is possible to detect the correct device node via matching attributes. Generic heads are marked as 'mei' while special purpose heads have their own names. Currently we are adding 'itouch' string for Intel IPTS 1.0, 2.0 devices. This is done via new sysfs attribute 'kind'. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Link: https://lore.kernel.org/r/20200728192242.3117779-1-tomas.winkler@intel.com Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/testing/sysfs-class-mei | 13 +++++++++++++ drivers/misc/mei/hw-me.c | 22 ++++++++++++++++++---- drivers/misc/mei/hw-me.h | 7 ++++++- drivers/misc/mei/main.c | 25 +++++++++++++++++++++++++ drivers/misc/mei/mei_dev.h | 4 ++++ drivers/misc/mei/pci-me.c | 10 +++++----- 6 files changed, 71 insertions(+), 10 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-class-mei b/Documentation/ABI/testing/sysfs-class-mei index e9dc110650ae..5c52372b43cb 100644 --- a/Documentation/ABI/testing/sysfs-class-mei +++ b/Documentation/ABI/testing/sysfs-class-mei @@ -90,3 +90,16 @@ Description: Display trc status register content The ME FW writes Glitch Detection HW (TRC) status information into trc status register for BIOS and OS to monitor fw health. + +What: /sys/class/mei/meiN/kind +Date: Jul 2020 +KernelVersion: 5.8 +Contact: Tomas Winkler +Description: Display kind of the device + + Generic devices are marked as "mei" + while special purpose have their own + names. + Available options: + - mei: generic mei device. + - itouch: itouch (ipts) mei device. diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index 7692b69abcb5..cda0829ac589 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -1430,6 +1430,9 @@ static bool mei_me_fw_type_sps(const struct pci_dev *pdev) return fw_type == PCI_CFG_HFS_3_FW_SKU_SPS; } +#define MEI_CFG_KIND_ITOUCH \ + .kind = "itouch" + #define MEI_CFG_FW_SPS \ .quirk_probe = mei_me_fw_type_sps @@ -1499,6 +1502,13 @@ static const struct mei_cfg mei_me_pch8_cfg = { MEI_CFG_FW_VER_SUPP, }; +/* PCH8 Lynx Point and newer devices - iTouch */ +static const struct mei_cfg mei_me_pch8_itouch_cfg = { + MEI_CFG_KIND_ITOUCH, + MEI_CFG_PCH8_HFS, + MEI_CFG_FW_VER_SUPP, +}; + /* PCH8 Lynx Point with quirk for SPS Firmware exclusion */ static const struct mei_cfg mei_me_pch8_sps_4_cfg = { MEI_CFG_PCH8_HFS, @@ -1528,10 +1538,11 @@ static const struct mei_cfg mei_me_pch12_sps_cfg = { MEI_CFG_FW_SPS, }; -/* Cannon Lake with quirk for SPS 5.0 and newer Firmware exclusion - * w/o DMA support +/* Cannon Lake itouch with quirk for SPS 5.0 and newer Firmware exclusion + * w/o DMA support. */ -static const struct mei_cfg mei_me_pch12_nodma_sps_cfg = { +static const struct mei_cfg mei_me_pch12_itouch_sps_cfg = { + MEI_CFG_KIND_ITOUCH, MEI_CFG_PCH8_HFS, MEI_CFG_FW_VER_SUPP, MEI_CFG_FW_SPS, @@ -1566,11 +1577,12 @@ static const struct mei_cfg *const mei_cfg_list[] = { [MEI_ME_PCH7_CFG] = &mei_me_pch7_cfg, [MEI_ME_PCH_CPT_PBG_CFG] = &mei_me_pch_cpt_pbg_cfg, [MEI_ME_PCH8_CFG] = &mei_me_pch8_cfg, + [MEI_ME_PCH8_ITOUCH_CFG] = &mei_me_pch8_itouch_cfg, [MEI_ME_PCH8_SPS_4_CFG] = &mei_me_pch8_sps_4_cfg, [MEI_ME_PCH12_CFG] = &mei_me_pch12_cfg, [MEI_ME_PCH12_SPS_4_CFG] = &mei_me_pch12_sps_4_cfg, [MEI_ME_PCH12_SPS_CFG] = &mei_me_pch12_sps_cfg, - [MEI_ME_PCH12_SPS_NODMA_CFG] = &mei_me_pch12_nodma_sps_cfg, + [MEI_ME_PCH12_SPS_ITOUCH_CFG] = &mei_me_pch12_itouch_sps_cfg, [MEI_ME_PCH15_CFG] = &mei_me_pch15_cfg, [MEI_ME_PCH15_SPS_CFG] = &mei_me_pch15_sps_cfg, }; @@ -1614,6 +1626,8 @@ struct mei_device *mei_me_dev_init(struct device *parent, dev->fw_f_fw_ver_supported = cfg->fw_ver_supported; + dev->kind = cfg->kind; + return dev; } diff --git a/drivers/misc/mei/hw-me.h b/drivers/misc/mei/hw-me.h index 560c8ebb17be..00a7132ac7a2 100644 --- a/drivers/misc/mei/hw-me.h +++ b/drivers/misc/mei/hw-me.h @@ -19,6 +19,7 @@ * * @fw_status: FW status * @quirk_probe: device exclusion quirk + * @kind: MEI head kind * @dma_size: device DMA buffers size * @fw_ver_supported: is fw version retrievable from FW * @hw_trc_supported: does the hw support trc register @@ -26,6 +27,7 @@ struct mei_cfg { const struct mei_fw_status fw_status; bool (*quirk_probe)(const struct pci_dev *pdev); + const char *kind; size_t dma_size[DMA_DSCR_NUM]; u32 fw_ver_supported:1; u32 hw_trc_supported:1; @@ -76,6 +78,8 @@ struct mei_me_hw { * with quirk for Node Manager exclusion. * @MEI_ME_PCH8_CFG: Platform Controller Hub Gen8 and newer * client platforms. + * @MEI_ME_PCH8_ITOUCH_CFG:Platform Controller Hub Gen8 and newer + * client platforms (iTouch). * @MEI_ME_PCH8_SPS_4_CFG: Platform Controller Hub Gen8 and newer * servers platforms with quirk for * SPS firmware exclusion. @@ -100,11 +104,12 @@ enum mei_cfg_idx { MEI_ME_PCH7_CFG, MEI_ME_PCH_CPT_PBG_CFG, MEI_ME_PCH8_CFG, + MEI_ME_PCH8_ITOUCH_CFG, MEI_ME_PCH8_SPS_4_CFG, MEI_ME_PCH12_CFG, MEI_ME_PCH12_SPS_4_CFG, MEI_ME_PCH12_SPS_CFG, - MEI_ME_PCH12_SPS_NODMA_CFG, + MEI_ME_PCH12_SPS_ITOUCH_CFG, MEI_ME_PCH15_CFG, MEI_ME_PCH15_SPS_CFG, MEI_ME_NUM_CFG, diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 05e6ad6d4d54..86ef5c1a7928 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -885,6 +885,30 @@ void mei_set_devstate(struct mei_device *dev, enum mei_dev_state state) } } +/** + * kind_show - display device kind + * + * @device: device pointer + * @attr: attribute pointer + * @buf: char out buffer + * + * Return: number of the bytes printed into buf or error + */ +static ssize_t kind_show(struct device *device, + struct device_attribute *attr, char *buf) +{ + struct mei_device *dev = dev_get_drvdata(device); + ssize_t ret; + + if (dev->kind) + ret = sprintf(buf, "%s\n", dev->kind); + else + ret = sprintf(buf, "%s\n", "mei"); + + return ret; +} +static DEVICE_ATTR_RO(kind); + static struct attribute *mei_attrs[] = { &dev_attr_fw_status.attr, &dev_attr_hbm_ver.attr, @@ -893,6 +917,7 @@ static struct attribute *mei_attrs[] = { &dev_attr_fw_ver.attr, &dev_attr_dev_state.attr, &dev_attr_trc.attr, + &dev_attr_kind.attr, NULL }; ATTRIBUTE_GROUPS(mei); diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 3a29db07211d..d3a4f54c0ae7 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -445,6 +445,8 @@ struct mei_fw_version { * @device_list : mei client bus list * @cl_bus_lock : client bus list lock * + * @kind : kind of mei device + * * @dbgfs_dir : debugfs mei root directory * * @ops: : hw specific operations @@ -528,6 +530,8 @@ struct mei_device { struct list_head device_list; struct mutex cl_bus_lock; + const char *kind; + #if IS_ENABLED(CONFIG_DEBUG_FS) struct dentry *dbgfs_dir; #endif /* CONFIG_DEBUG_FS */ diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index 159e40a2505d..1de9ef7a272b 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c @@ -68,7 +68,7 @@ static const struct pci_device_id mei_me_pci_tbl[] = { {MEI_PCI_DEVICE(MEI_DEV_ID_SPT, MEI_ME_PCH8_CFG)}, {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_2, MEI_ME_PCH8_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_3, MEI_ME_PCH8_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_3, MEI_ME_PCH8_ITOUCH_CFG)}, {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_H, MEI_ME_PCH8_SPS_4_CFG)}, {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_H_2, MEI_ME_PCH8_SPS_4_CFG)}, {MEI_PCI_DEVICE(MEI_DEV_ID_LBG, MEI_ME_PCH12_SPS_4_CFG)}, @@ -85,15 +85,15 @@ static const struct pci_device_id mei_me_pci_tbl[] = { {MEI_PCI_DEVICE(MEI_DEV_ID_KBP_3, MEI_ME_PCH8_CFG)}, {MEI_PCI_DEVICE(MEI_DEV_ID_CNP_LP, MEI_ME_PCH12_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_CNP_LP_3, MEI_ME_PCH8_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_CNP_LP_3, MEI_ME_PCH8_ITOUCH_CFG)}, {MEI_PCI_DEVICE(MEI_DEV_ID_CNP_H, MEI_ME_PCH12_SPS_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_CNP_H_3, MEI_ME_PCH12_SPS_NODMA_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_CNP_H_3, MEI_ME_PCH12_SPS_ITOUCH_CFG)}, {MEI_PCI_DEVICE(MEI_DEV_ID_CMP_LP, MEI_ME_PCH12_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_CMP_LP_3, MEI_ME_PCH8_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_CMP_LP_3, MEI_ME_PCH8_ITOUCH_CFG)}, {MEI_PCI_DEVICE(MEI_DEV_ID_CMP_V, MEI_ME_PCH12_CFG)}, {MEI_PCI_DEVICE(MEI_DEV_ID_CMP_H, MEI_ME_PCH12_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_CMP_H_3, MEI_ME_PCH8_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_CMP_H_3, MEI_ME_PCH8_ITOUCH_CFG)}, {MEI_PCI_DEVICE(MEI_DEV_ID_ICP_LP, MEI_ME_PCH12_CFG)}, -- cgit v1.2.3-59-g8ed1b From 37b8b73f641cc151a58eef8b9a73dac2f273146e Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Mon, 27 Jul 2020 13:32:58 -0500 Subject: greybus: Use fallthrough pseudo-keyword Replace the existing /* fall through */ comments and its variants with the new pseudo-keyword macro fallthrough[1]. [1] https://www.kernel.org/doc/html/v5.7/process/deprecated.html?highlight=fallthrough#implicit-switch-case-fall-through Signed-off-by: Gustavo A. R. Silva Link: https://lore.kernel.org/r/20200727183258.GA28571@embeddedor Signed-off-by: Greg Kroah-Hartman --- drivers/greybus/es2.c | 2 +- drivers/greybus/interface.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/greybus/es2.c b/drivers/greybus/es2.c index 366716f11b1a..1df6ab5d339d 100644 --- a/drivers/greybus/es2.c +++ b/drivers/greybus/es2.c @@ -759,7 +759,7 @@ static int check_urb_status(struct urb *urb) case -EOVERFLOW: dev_err(dev, "%s: overflow actual length is %d\n", __func__, urb->actual_length); - /* fall through */ + fallthrough; case -ECONNRESET: case -ENOENT: case -ESHUTDOWN: diff --git a/drivers/greybus/interface.c b/drivers/greybus/interface.c index 67dbe6fda9a1..58ea374d8aaa 100644 --- a/drivers/greybus/interface.c +++ b/drivers/greybus/interface.c @@ -1233,7 +1233,7 @@ int gb_interface_add(struct gb_interface *intf) case GB_INTERFACE_TYPE_GREYBUS: dev_info(&intf->dev, "GMP VID=0x%08x, PID=0x%08x\n", intf->vendor_id, intf->product_id); - /* fall-through */ + fallthrough; case GB_INTERFACE_TYPE_UNIPRO: dev_info(&intf->dev, "DDBL1 Manufacturer=0x%08x, Product=0x%08x\n", intf->ddbl1_manufacturer_id, -- cgit v1.2.3-59-g8ed1b From 4b836a1426cb0f1ef2a6e211d7e553221594f8fc Mon Sep 17 00:00:00 2001 From: Jann Horn Date: Mon, 27 Jul 2020 14:04:24 +0200 Subject: binder: Prevent context manager from incrementing ref 0 Binder is designed such that a binder_proc never has references to itself. If this rule is violated, memory corruption can occur when a process sends a transaction to itself; see e.g. . There is a remaining edgecase through which such a transaction-to-self can still occur from the context of a task with BINDER_SET_CONTEXT_MGR access: - task A opens /dev/binder twice, creating binder_proc instances P1 and P2 - P1 becomes context manager - P2 calls ACQUIRE on the magic handle 0, allocating index 0 in its handle table - P1 dies (by closing the /dev/binder fd and waiting a bit) - P2 becomes context manager - P2 calls ACQUIRE on the magic handle 0, allocating index 1 in its handle table [this triggers a warning: "binder: 1974:1974 tried to acquire reference to desc 0, got 1 instead"] - task B opens /dev/binder once, creating binder_proc instance P3 - P3 calls P2 (via magic handle 0) with (void*)1 as argument (two-way transaction) - P2 receives the handle and uses it to call P3 (two-way transaction) - P3 calls P2 (via magic handle 0) (two-way transaction) - P2 calls P2 (via handle 1) (two-way transaction) And then, if P2 does *NOT* accept the incoming transaction work, but instead closes the binder fd, we get a crash. Solve it by preventing the context manager from using ACQUIRE on ref 0. There shouldn't be any legitimate reason for the context manager to do that. Additionally, print a warning if someone manages to find another way to trigger a transaction-to-self bug in the future. Cc: stable@vger.kernel.org Fixes: 457b9a6f09f0 ("Staging: android: add binder driver") Acked-by: Todd Kjos Signed-off-by: Jann Horn Reviewed-by: Martijn Coenen Link: https://lore.kernel.org/r/20200727120424.1627555-1-jannh@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index f50c5f182bb5..5b310eea9e52 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -2982,6 +2982,12 @@ static void binder_transaction(struct binder_proc *proc, goto err_dead_binder; } e->to_node = target_node->debug_id; + if (WARN_ON(proc == target_proc)) { + return_error = BR_FAILED_REPLY; + return_error_param = -EINVAL; + return_error_line = __LINE__; + goto err_invalid_target_handle; + } if (security_binder_transaction(proc->tsk, target_proc->tsk) < 0) { return_error = BR_FAILED_REPLY; @@ -3635,10 +3641,17 @@ static int binder_thread_write(struct binder_proc *proc, struct binder_node *ctx_mgr_node; mutex_lock(&context->context_mgr_node_lock); ctx_mgr_node = context->binder_context_mgr_node; - if (ctx_mgr_node) + if (ctx_mgr_node) { + if (ctx_mgr_node->proc == proc) { + binder_user_error("%d:%d context manager tried to acquire desc 0\n", + proc->pid, thread->pid); + mutex_unlock(&context->context_mgr_node_lock); + return -EINVAL; + } ret = binder_inc_ref_for_node( proc, ctx_mgr_node, strong, NULL, &rdata); + } mutex_unlock(&context->context_mgr_node_lock); } if (ret) -- cgit v1.2.3-59-g8ed1b From 4df9772c8489b7d626db0ee307e029aea66db260 Mon Sep 17 00:00:00 2001 From: Mrinal Pandey Date: Fri, 24 Jul 2020 18:42:54 +0530 Subject: drivers: android: Fix a variable declaration coding style issue Add a blank line after variable declarations as suggested by checkpatch. Signed-off-by: Mrinal Pandey Link: https://lore.kernel.org/r/20200724131254.qxbvderrws36dzzq@mrinalpandey Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder_alloc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/android/binder_alloc.c b/drivers/android/binder_alloc.c index cbe6aa77d50d..69609696a843 100644 --- a/drivers/android/binder_alloc.c +++ b/drivers/android/binder_alloc.c @@ -547,6 +547,7 @@ static void binder_delete_free_buffer(struct binder_alloc *alloc, { struct binder_buffer *prev, *next = NULL; bool to_free = true; + BUG_ON(alloc->buffers.next == &buffer->entry); prev = binder_buffer_prev(buffer); BUG_ON(!prev->free); -- cgit v1.2.3-59-g8ed1b From 72b93c79dbbe261371abb1bf3cf7302cfe31e8d9 Mon Sep 17 00:00:00 2001 From: Mrinal Pandey Date: Fri, 24 Jul 2020 18:43:48 +0530 Subject: drivers: android: Remove the use of else after return Remove the unnecessary else branch after return statement as suggested by checkpatch. Signed-off-by: Mrinal Pandey Link: https://lore.kernel.org/r/20200724131348.haz4ocxcferdcsgn@mrinalpandey Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 5b310eea9e52..2123655ebb7c 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -1969,9 +1969,8 @@ static void binder_send_failed_reply(struct binder_transaction *t, binder_thread_dec_tmpref(target_thread); binder_free_transaction(t); return; - } else { - __release(&target_thread->proc->inner_lock); } + __release(&target_thread->proc->inner_lock); next = t->from_parent; binder_debug(BINDER_DEBUG_FAILED_TRANSACTION, -- cgit v1.2.3-59-g8ed1b From 8df5b9492202e9cac9917465e945fcf478d55404 Mon Sep 17 00:00:00 2001 From: Mrinal Pandey Date: Fri, 24 Jul 2020 18:44:03 +0530 Subject: drivers: android: Remove braces for a single statement if-else block Remove braces for both if and else block as suggested by checkpatch. Signed-off-by: Mrinal Pandey Link: https://lore.kernel.org/r/20200724131403.dahfhdwa3wirzkxj@mrinalpandey Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 2123655ebb7c..f936530a19b0 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -2759,11 +2759,10 @@ static bool binder_proc_transaction(struct binder_transaction *t, binder_node_lock(node); if (oneway) { BUG_ON(thread); - if (node->has_async_transaction) { + if (node->has_async_transaction) pending_async = true; - } else { + else node->has_async_transaction = true; - } } binder_inner_proc_lock(proc); -- cgit v1.2.3-59-g8ed1b From 81195f9689ac16c01c894c756b925e28e546b123 Mon Sep 17 00:00:00 2001 From: Mrinal Pandey Date: Fri, 24 Jul 2020 18:44:33 +0530 Subject: drivers: android: Fix a variable declaration coding style issue Add a blank line after variable declarations as suggested by checkpatch. Signed-off-by: Mrinal Pandey Link: https://lore.kernel.org/r/20200724131433.stf3ycooogawyzb3@mrinalpandey Signed-off-by: Greg Kroah-Hartman --- drivers/android/binderfs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/android/binderfs.c b/drivers/android/binderfs.c index 7cf566aafe1f..e218360de58d 100644 --- a/drivers/android/binderfs.c +++ b/drivers/android/binderfs.c @@ -351,6 +351,7 @@ static const struct super_operations binderfs_super_ops = { static inline bool is_binderfs_control_device(const struct dentry *dentry) { struct binderfs_info *info = dentry->d_sb->s_fs_info; + return info->control_dentry == dentry; } -- cgit v1.2.3-59-g8ed1b From 7e84522cd089c6ef3e6adc7f1c9a5b2f705ccd9b Mon Sep 17 00:00:00 2001 From: Mrinal Pandey Date: Fri, 24 Jul 2020 18:44:49 +0530 Subject: drivers: android: Fix the SPDX comment style C source files should have `//` as SPDX comment and not `/**/`. Fix this by running checkpatch on the file. Signed-off-by: Mrinal Pandey Link: https://lore.kernel.org/r/20200724131449.zvjutbemg3vqhrzh@mrinalpandey Signed-off-by: Greg Kroah-Hartman --- drivers/android/binderfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/android/binderfs.c b/drivers/android/binderfs.c index e218360de58d..7b76fefde3f8 100644 --- a/drivers/android/binderfs.c +++ b/drivers/android/binderfs.c @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0 */ +// SPDX-License-Identifier: GPL-2.0 #include #include -- cgit v1.2.3-59-g8ed1b From bcd14bb7a68520bf88e45e91d354e43535624f82 Mon Sep 17 00:00:00 2001 From: Tiezhu Yang Date: Wed, 22 Jul 2020 11:06:52 +0100 Subject: nvmem: sprd: Fix return value of sprd_efuse_probe() When call function devm_platform_ioremap_resource(), we should use IS_ERR() to check the return value and return PTR_ERR() if failed. Fixes: 096030e7f449 ("nvmem: sprd: Add Spreadtrum SoCs eFuse support") Signed-off-by: Tiezhu Yang Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20200722100705.7772-2-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/sprd-efuse.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/nvmem/sprd-efuse.c b/drivers/nvmem/sprd-efuse.c index 925feb21d5ad..59523245db8a 100644 --- a/drivers/nvmem/sprd-efuse.c +++ b/drivers/nvmem/sprd-efuse.c @@ -378,8 +378,8 @@ static int sprd_efuse_probe(struct platform_device *pdev) return -ENOMEM; efuse->base = devm_platform_ioremap_resource(pdev, 0); - if (!efuse->base) - return -ENOMEM; + if (IS_ERR(efuse->base)) + return PTR_ERR(efuse->base); ret = of_hwspin_lock_get_id(np, 0); if (ret < 0) { -- cgit v1.2.3-59-g8ed1b From a9c4a155d2d4619eb39798891fb5fc81739c58fd Mon Sep 17 00:00:00 2001 From: Tiezhu Yang Date: Wed, 22 Jul 2020 11:06:53 +0100 Subject: MAINTAINERS: Add git tree for NVMEM FRAMEWORK There is no git tree for NVMEM FRAMEWORK in MAINTAINERS, it is not convinent to rebase, add it. Signed-off-by: Tiezhu Yang Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20200722100705.7772-3-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index dbe6a71eb6f2..7e13674a6491 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12266,6 +12266,7 @@ F: drivers/nvme/target/ NVMEM FRAMEWORK M: Srinivas Kandagatla S: Maintained +T: git git://git.kernel.org/pub/scm/linux/kernel/git/srini/nvmem.git F: Documentation/ABI/stable/sysfs-bus-nvmem F: Documentation/devicetree/bindings/nvmem/ F: drivers/nvmem/ -- cgit v1.2.3-59-g8ed1b From 835667157461e65399654cb726bad2075ddab26c Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Wed, 22 Jul 2020 11:06:54 +0100 Subject: nvmem: Enforce nvmem stride in the sysfs interface The 'struct nvmem_config' has a stride attribute that specifies the needed alignment for accesses into the nvmem. This is used in nvmem_cell_info_to_nvmem_cell() but not in the sysfs read/write functions. If the alignment is important in one place it's important everywhere, so let's add enforcement. For now we'll consider it totally invalid to access with the wrong alignment. We could relax this in the read case where we could just read some extra bytes and throw them away. Relaxing it in the write case seems harder (and less safe?) since we'd have to read some data first and then write it back. To keep it symmetric we'll just disallow it in both cases. Reported-by: Ravi Kumar Bokka Signed-off-by: Douglas Anderson Reviewed-by: Ravi Kumar Bokka Tested-by: Ravi Kumar Bokka Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20200722100705.7772-4-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/core.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index 927eb5f6003f..fc480d636be2 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -135,6 +135,9 @@ static ssize_t bin_attr_nvmem_read(struct file *filp, struct kobject *kobj, if (pos >= nvmem->size) return 0; + if (!IS_ALIGNED(pos, nvmem->stride)) + return -EINVAL; + if (count < nvmem->word_size) return -EINVAL; @@ -172,6 +175,9 @@ static ssize_t bin_attr_nvmem_write(struct file *filp, struct kobject *kobj, if (pos >= nvmem->size) return -EFBIG; + if (!IS_ALIGNED(pos, nvmem->stride)) + return -EINVAL; + if (count < nvmem->word_size) return -EINVAL; -- cgit v1.2.3-59-g8ed1b From 2eef018efb9624b8717945f5e2ea0a1965520956 Mon Sep 17 00:00:00 2001 From: Freeman Liu Date: Wed, 22 Jul 2020 11:06:55 +0100 Subject: nvmem: sc27xx: add sc2730 efuse support Add support to the new efuse IP which is integrated in the SC2730 which includes multiple blocks in a single chip. Signed-off-by: Freeman Liu Signed-off-by: Chunyan Zhang Reviewed-by: Baolin Wang Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20200722100705.7772-5-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/sc27xx-efuse.c | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/drivers/nvmem/sc27xx-efuse.c b/drivers/nvmem/sc27xx-efuse.c index ab5e7e0bc3d8..c825fc902d10 100644 --- a/drivers/nvmem/sc27xx-efuse.c +++ b/drivers/nvmem/sc27xx-efuse.c @@ -4,12 +4,14 @@ #include #include #include +#include #include #include #include /* PMIC global registers definition */ #define SC27XX_MODULE_EN 0xc08 +#define SC2730_MODULE_EN 0x1808 #define SC27XX_EFUSE_EN BIT(6) /* Efuse controller registers definition */ @@ -49,12 +51,29 @@ #define SC27XX_EFUSE_POLL_TIMEOUT 3000000 #define SC27XX_EFUSE_POLL_DELAY_US 10000 +/* + * Since different PMICs of SC27xx series can have different + * address , we should save address in the device data structure. + */ +struct sc27xx_efuse_variant_data { + u32 module_en; +}; + struct sc27xx_efuse { struct device *dev; struct regmap *regmap; struct hwspinlock *hwlock; struct mutex mutex; u32 base; + const struct sc27xx_efuse_variant_data *var_data; +}; + +static const struct sc27xx_efuse_variant_data sc2731_edata = { + .module_en = SC27XX_MODULE_EN, +}; + +static const struct sc27xx_efuse_variant_data sc2730_edata = { + .module_en = SC2730_MODULE_EN, }; /* @@ -119,7 +138,7 @@ static int sc27xx_efuse_read(void *context, u32 offset, void *val, size_t bytes) return ret; /* Enable the efuse controller. */ - ret = regmap_update_bits(efuse->regmap, SC27XX_MODULE_EN, + ret = regmap_update_bits(efuse->regmap, efuse->var_data->module_en, SC27XX_EFUSE_EN, SC27XX_EFUSE_EN); if (ret) goto unlock_efuse; @@ -169,7 +188,7 @@ static int sc27xx_efuse_read(void *context, u32 offset, void *val, size_t bytes) disable_efuse: /* Disable the efuse controller after reading. */ - regmap_update_bits(efuse->regmap, SC27XX_MODULE_EN, SC27XX_EFUSE_EN, 0); + regmap_update_bits(efuse->regmap, efuse->var_data->module_en, SC27XX_EFUSE_EN, 0); unlock_efuse: sc27xx_efuse_unlock(efuse); @@ -219,6 +238,7 @@ static int sc27xx_efuse_probe(struct platform_device *pdev) mutex_init(&efuse->mutex); efuse->dev = &pdev->dev; + efuse->var_data = of_device_get_match_data(&pdev->dev); econfig.stride = 1; econfig.word_size = 1; @@ -238,7 +258,8 @@ static int sc27xx_efuse_probe(struct platform_device *pdev) } static const struct of_device_id sc27xx_efuse_of_match[] = { - { .compatible = "sprd,sc2731-efuse" }, + { .compatible = "sprd,sc2731-efuse", .data = &sc2731_edata}, + { .compatible = "sprd,sc2730-efuse", .data = &sc2730_edata}, { } }; -- cgit v1.2.3-59-g8ed1b From 3a7580710b0344fd2d2fc9e988620fddd187b83e Mon Sep 17 00:00:00 2001 From: Andreas Färber Date: Wed, 22 Jul 2020 11:06:56 +0100 Subject: nvmem: core: Grammar fixes for help text MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It's "an unsigned" but "a U". Similarly, "an entry" but "a binary entry". While at it, also drop superfluous articles for negative and zero. Signed-off-by: Andreas Färber Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20200722100705.7772-6-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/core.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index fc480d636be2..95bed31391cd 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -573,7 +573,7 @@ static int nvmem_add_cells_from_of(struct nvmem_device *nvmem) /** * nvmem_register() - Register a nvmem device for given nvmem_config. - * Also creates an binary entry in /sys/bus/nvmem/devices/dev-name/nvmem + * Also creates a binary entry in /sys/bus/nvmem/devices/dev-name/nvmem * * @config: nvmem device configuration with which nvmem device is created. * @@ -728,7 +728,7 @@ static void devm_nvmem_release(struct device *dev, void *res) /** * devm_nvmem_register() - Register a managed nvmem device for given * nvmem_config. - * Also creates an binary entry in /sys/bus/nvmem/devices/dev-name/nvmem + * Also creates a binary entry in /sys/bus/nvmem/devices/dev-name/nvmem * * @dev: Device that uses the nvmem device. * @config: nvmem device configuration with which nvmem device is created. @@ -772,7 +772,7 @@ static int devm_nvmem_match(struct device *dev, void *res, void *data) * @dev: Device that uses the nvmem device. * @nvmem: Pointer to previously registered nvmem device. * - * Return: Will be an negative on error or a zero on success. + * Return: Will be negative on error or zero on success. */ int devm_nvmem_unregister(struct device *dev, struct nvmem_device *nvmem) { @@ -1375,7 +1375,7 @@ static int nvmem_cell_read_common(struct device *dev, const char *cell_id, } /** - * nvmem_cell_read_u16() - Read a cell value as an u16 + * nvmem_cell_read_u16() - Read a cell value as a u16 * * @dev: Device that requests the nvmem cell. * @cell_id: Name of nvmem cell to read. @@ -1390,7 +1390,7 @@ int nvmem_cell_read_u16(struct device *dev, const char *cell_id, u16 *val) EXPORT_SYMBOL_GPL(nvmem_cell_read_u16); /** - * nvmem_cell_read_u32() - Read a cell value as an u32 + * nvmem_cell_read_u32() - Read a cell value as a u32 * * @dev: Device that requests the nvmem cell. * @cell_id: Name of nvmem cell to read. @@ -1405,7 +1405,7 @@ int nvmem_cell_read_u32(struct device *dev, const char *cell_id, u32 *val) EXPORT_SYMBOL_GPL(nvmem_cell_read_u32); /** - * nvmem_cell_read_u64() - Read a cell value as an u64 + * nvmem_cell_read_u64() - Read a cell value as a u64 * * @dev: Device that requests the nvmem cell. * @cell_id: Name of nvmem cell to read. -- cgit v1.2.3-59-g8ed1b From 5037d368b2c2c90e9432d477e5562dce1c33d5c9 Mon Sep 17 00:00:00 2001 From: Andreas Färber Date: Wed, 22 Jul 2020 11:06:57 +0100 Subject: nvmem: core: Add nvmem_cell_read_u8() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Complement the u16, u32 and u64 helpers with a u8 variant to ease accessing byte-sized values. This helper will be useful for Realtek Digital Home Center platforms, which store some byte and sub-byte sized values in non-volatile memory. Signed-off-by: Andreas Färber Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20200722100705.7772-7-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/core.c | 15 +++++++++++++++ include/linux/nvmem-consumer.h | 1 + 2 files changed, 16 insertions(+) diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index 95bed31391cd..d6bacc878500 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -1374,6 +1374,21 @@ static int nvmem_cell_read_common(struct device *dev, const char *cell_id, return 0; } +/** + * nvmem_cell_read_u8() - Read a cell value as a u8 + * + * @dev: Device that requests the nvmem cell. + * @cell_id: Name of nvmem cell to read. + * @val: pointer to output value. + * + * Return: 0 on success or negative errno. + */ +int nvmem_cell_read_u8(struct device *dev, const char *cell_id, u8 *val) +{ + return nvmem_cell_read_common(dev, cell_id, val, sizeof(*val)); +} +EXPORT_SYMBOL_GPL(nvmem_cell_read_u8); + /** * nvmem_cell_read_u16() - Read a cell value as a u16 * diff --git a/include/linux/nvmem-consumer.h b/include/linux/nvmem-consumer.h index 1b311d27c9b8..052293f4cbdb 100644 --- a/include/linux/nvmem-consumer.h +++ b/include/linux/nvmem-consumer.h @@ -61,6 +61,7 @@ void nvmem_cell_put(struct nvmem_cell *cell); void devm_nvmem_cell_put(struct device *dev, struct nvmem_cell *cell); void *nvmem_cell_read(struct nvmem_cell *cell, size_t *len); int nvmem_cell_write(struct nvmem_cell *cell, void *buf, size_t len); +int nvmem_cell_read_u8(struct device *dev, const char *cell_id, u8 *val); int nvmem_cell_read_u16(struct device *dev, const char *cell_id, u16 *val); int nvmem_cell_read_u32(struct device *dev, const char *cell_id, u32 *val); int nvmem_cell_read_u64(struct device *dev, const char *cell_id, u64 *val); -- cgit v1.2.3-59-g8ed1b From 731aa3fae8137ebca83a01d20fbf3effb4798fc8 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Wed, 22 Jul 2020 11:06:58 +0100 Subject: nvmem: core: add support to auto devid For nvmem providers which have multiple instances, it is required to suffix the provider name with proper id, so that they do not confict for the same name. Currently the core does not handle this case properly eventhough core already has logic to generate the id. This patch add new devid type NVMEM_DEVID_AUTO for providers to be able to allow core to assign id and append it to provier name. Reported-by: Shawn Guo Signed-off-by: Srinivas Kandagatla Tested-by: Shawn Guo Link: https://lore.kernel.org/r/20200722100705.7772-8-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/core.c | 10 ++++++++-- include/linux/nvmem-provider.h | 3 +++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index d6bacc878500..6cd3edb2eaf6 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -635,12 +635,18 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config) if (!config->no_of_node) nvmem->dev.of_node = config->dev->of_node; - if (config->id == -1 && config->name) { + switch (config->id) { + case NVMEM_DEVID_NONE: dev_set_name(&nvmem->dev, "%s", config->name); - } else { + break; + case NVMEM_DEVID_AUTO: + dev_set_name(&nvmem->dev, "%s%d", config->name, nvmem->id); + break; + default: dev_set_name(&nvmem->dev, "%s%d", config->name ? : "nvmem", config->name ? config->id : nvmem->id); + break; } nvmem->read_only = device_property_present(config->dev, "read-only") || diff --git a/include/linux/nvmem-provider.h b/include/linux/nvmem-provider.h index 6d6f8e5d24c9..06409a6c40bc 100644 --- a/include/linux/nvmem-provider.h +++ b/include/linux/nvmem-provider.h @@ -27,6 +27,9 @@ enum nvmem_type { NVMEM_TYPE_BATTERY_BACKED, }; +#define NVMEM_DEVID_NONE (-1) +#define NVMEM_DEVID_AUTO (-2) + /** * struct nvmem_config - NVMEM device configuration * -- cgit v1.2.3-59-g8ed1b From 3f07c658330f2bc00a5e86ac2c8ec2c659d73760 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Wed, 22 Jul 2020 11:06:59 +0100 Subject: nvmem: qfprom: use NVMEM_DEVID_AUTO for multiple instances There could be multiple qfprom devices on some SoCs. For example, on MSM8939, qfprom@58000 holds efuse bits for Core Power Reduction (CPR), and qfprom@5c000 holds bits for TSENS. Registering multiple nvmem devices with the same id results in the following failure on the second device. [ 1.682731] sysfs: cannot create duplicate filename /bus/nvmem/devices/qfprom0 [ 1.685889] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 4.19.107-00567-g11c887ce2838-dirty #334 [ 1.693191] Hardware name: Square, Inc. T2 Devkit (DT) [ 1.701610] Call trace: [ 1.706673] dump_backtrace+0x0/0x158 [ 1.708987] show_stack+0x14/0x20 [ 1.712810] dump_stack+0x98/0xbc [ 1.716114] sysfs_warn_dup+0x60/0x78 [ 1.719401] sysfs_do_create_link_sd.isra.0+0xdc/0xe8 [ 1.723047] sysfs_create_link+0x20/0x40 [ 1.728088] bus_add_device+0x68/0x130 [ 1.732083] device_add+0x3f8/0x628 [ 1.735639] nvmem_register.part.4+0x150/0x348 [ 1.739018] devm_nvmem_register+0x4c/0xa8 [ 1.743532] qfprom_probe+0x94/0xb8 [ 1.747615] platform_drv_probe+0x50/0xa0 [ 1.750998] really_probe+0x1b8/0x298 [ 1.755164] driver_probe_device+0x58/0x100 [ 1.758810] __driver_attach+0xe0/0xe8 [ 1.762802] bus_for_each_dev+0x74/0xc8 [ 1.766622] driver_attach+0x20/0x28 [ 1.770354] bus_add_driver+0x1ac/0x218 [ 1.774175] driver_register+0x60/0x110 [ 1.777734] __platform_driver_register+0x40/0x48 [ 1.781570] qfprom_driver_init+0x18/0x20 [ 1.786416] do_one_initcall+0x5c/0x178 [ 1.790418] kernel_init_freeable+0x198/0x244 [ 1.794062] kernel_init+0x10/0x108 [ 1.798567] ret_from_fork+0x10/0x18 [ 1.802084] qcom,qfprom: probe of 5c000.qfprom failed with error -17 Fix this issue by using NVMEM_DEVID_AUTO. Reported-by: Shawn Guo Signed-off-by: Srinivas Kandagatla Tested-by: Shawn Guo Link: https://lore.kernel.org/r/20200722100705.7772-9-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/qfprom.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/nvmem/qfprom.c b/drivers/nvmem/qfprom.c index 8a91717600be..8b425f8d847d 100644 --- a/drivers/nvmem/qfprom.c +++ b/drivers/nvmem/qfprom.c @@ -31,6 +31,7 @@ static struct nvmem_config econfig = { .name = "qfprom", .stride = 1, .word_size = 1, + .id = NVMEM_DEVID_AUTO, .reg_read = qfprom_reg_read, }; -- cgit v1.2.3-59-g8ed1b From 83281b7e22f785b831c49c93cce1a8e28dc93787 Mon Sep 17 00:00:00 2001 From: Ravi Kumar Bokka Date: Wed, 22 Jul 2020 11:07:00 +0100 Subject: dt-bindings: nvmem: qfprom: Convert to yaml This switches the bindings over from txt to yaml. Signed-off-by: Ravi Kumar Bokka Signed-off-by: Douglas Anderson Reviewed-by: Rob Herring Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20200722100705.7772-10-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/nvmem/qcom,qfprom.yaml | 50 ++++++++++++++++++++++ Documentation/devicetree/bindings/nvmem/qfprom.txt | 35 --------------- 2 files changed, 50 insertions(+), 35 deletions(-) create mode 100644 Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml delete mode 100644 Documentation/devicetree/bindings/nvmem/qfprom.txt diff --git a/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml b/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml new file mode 100644 index 000000000000..39f97c1c83a4 --- /dev/null +++ b/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml @@ -0,0 +1,50 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/nvmem/qcom,qfprom.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Technologies Inc, QFPROM Efuse bindings + +maintainers: + - Srinivas Kandagatla + +allOf: + - $ref: "nvmem.yaml#" + +properties: + compatible: + const: qcom,qfprom + + reg: + items: + - description: The corrected region. + + # Needed if any child nodes are present. + "#address-cells": + const: 1 + "#size-cells": + const: 1 + +required: + - compatible + - reg + +examples: + - | + soc { + #address-cells = <2>; + #size-cells = <2>; + + efuse@784000 { + compatible = "qcom,qfprom"; + reg = <0 0x00784000 0 0x8ff>; + #address-cells = <1>; + #size-cells = <1>; + + hstx-trim-primary@1eb { + reg = <0x1eb 0x1>; + bits = <1 4>; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/nvmem/qfprom.txt b/Documentation/devicetree/bindings/nvmem/qfprom.txt deleted file mode 100644 index 26fe878d5c86..000000000000 --- a/Documentation/devicetree/bindings/nvmem/qfprom.txt +++ /dev/null @@ -1,35 +0,0 @@ -= Qualcomm QFPROM device tree bindings = - -This binding is intended to represent QFPROM which is found in most QCOM SOCs. - -Required properties: -- compatible: should be "qcom,qfprom" -- reg: Should contain registers location and length - -= Data cells = -Are child nodes of qfprom, bindings of which as described in -bindings/nvmem/nvmem.txt - -Example: - - qfprom: qfprom@700000 { - compatible = "qcom,qfprom"; - reg = <0x00700000 0x8000>; - ... - /* Data cells */ - tsens_calibration: calib@404 { - reg = <0x4404 0x10>; - }; - }; - - -= Data consumers = -Are device nodes which consume nvmem data cells. - -For example: - - tsens { - ... - nvmem-cells = <&tsens_calibration>; - nvmem-cell-names = "calibration"; - }; -- cgit v1.2.3-59-g8ed1b From a37a15f748b41a66e1e8895f8841ab1c87e83233 Mon Sep 17 00:00:00 2001 From: Ravi Kumar Bokka Date: Wed, 22 Jul 2020 11:07:01 +0100 Subject: dt-bindings: nvmem: Add properties needed for blowing fuses On some systems it's possible to actually blow the fuses in the qfprom from the kernel. Add properties to support that. NOTE: Whether this is possible depends on the BIOS settings and whether the kernel has permissions here, so not all boards will be able to blow fuses in the kernel. Signed-off-by: Ravi Kumar Bokka Signed-off-by: Douglas Anderson Reviewed-by: Rob Herring Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20200722100705.7772-11-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/nvmem/qcom,qfprom.yaml | 50 +++++++++++++++++++++- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml b/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml index 39f97c1c83a4..d10a0cf91ba7 100644 --- a/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml +++ b/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml @@ -17,8 +17,27 @@ properties: const: qcom,qfprom reg: - items: - - description: The corrected region. + # If the QFPROM is read-only OS image then only the corrected region + # needs to be provided. If the QFPROM is writable then all 4 regions + # must be provided. + oneOf: + - items: + - description: The corrected region. + - items: + - description: The corrected region. + - description: The raw region. + - description: The config region. + - description: The security control region. + + # Clock must be provided if QFPROM is writable from the OS image. + clocks: + maxItems: 1 + clock-names: + const: core + + # Supply reference must be provided if QFPROM is writable from the OS image. + vcc-supply: + description: Our power supply. # Needed if any child nodes are present. "#address-cells": @@ -31,6 +50,33 @@ required: - reg examples: + - | + #include + + soc { + #address-cells = <2>; + #size-cells = <2>; + + efuse@784000 { + compatible = "qcom,qfprom"; + reg = <0 0x00784000 0 0x8ff>, + <0 0x00780000 0 0x7a0>, + <0 0x00782000 0 0x100>, + <0 0x00786000 0 0x1fff>; + clocks = <&gcc GCC_SEC_CTRL_CLK_SRC>; + clock-names = "core"; + #address-cells = <1>; + #size-cells = <1>; + + vcc-supply = <&vreg_l11a_1p8>; + + hstx-trim-primary@25b { + reg = <0x25b 0x1>; + bits = <1 3>; + }; + }; + }; + - | soc { #address-cells = <2>; -- cgit v1.2.3-59-g8ed1b From 93b4e49f8c861cb701bfb57bac703c846b83db3b Mon Sep 17 00:00:00 2001 From: Ravi Kumar Bokka Date: Wed, 22 Jul 2020 11:07:02 +0100 Subject: nvmem: qfprom: Add fuse blowing support This patch adds support for blowing fuses to the qfprom driver if the required properties are defined in the device tree. [Srini: Fixed merge conflict with AUTO ID] Signed-off-by: Ravi Kumar Bokka Signed-off-by: Douglas Anderson Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20200722100705.7772-12-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/qfprom.c | 316 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 304 insertions(+), 12 deletions(-) diff --git a/drivers/nvmem/qfprom.c b/drivers/nvmem/qfprom.c index 8b425f8d847d..5e9e60e2e591 100644 --- a/drivers/nvmem/qfprom.c +++ b/drivers/nvmem/qfprom.c @@ -3,58 +3,350 @@ * Copyright (C) 2015 Srinivas Kandagatla */ +#include #include +#include +#include +#include #include #include -#include #include #include +#include + +/* Blow timer clock frequency in Mhz */ +#define QFPROM_BLOW_TIMER_OFFSET 0x03c + +/* Amount of time required to hold charge to blow fuse in micro-seconds */ +#define QFPROM_FUSE_BLOW_POLL_US 100 +#define QFPROM_FUSE_BLOW_TIMEOUT_US 1000 + +#define QFPROM_BLOW_STATUS_OFFSET 0x048 +#define QFPROM_BLOW_STATUS_BUSY 0x1 +#define QFPROM_BLOW_STATUS_READY 0x0 + +#define QFPROM_ACCEL_OFFSET 0x044 + +#define QFPROM_VERSION_OFFSET 0x0 +#define QFPROM_MAJOR_VERSION_SHIFT 28 +#define QFPROM_MAJOR_VERSION_MASK GENMASK(31, QFPROM_MAJOR_VERSION_SHIFT) +#define QFPROM_MINOR_VERSION_SHIFT 16 +#define QFPROM_MINOR_VERSION_MASK GENMASK(27, QFPROM_MINOR_VERSION_SHIFT) + +static bool read_raw_data; +module_param(read_raw_data, bool, 0644); +MODULE_PARM_DESC(read_raw_data, "Read raw instead of corrected data"); +/** + * struct qfprom_soc_data - config that varies from SoC to SoC. + * + * @accel_value: Should contain qfprom accel value. + * @qfprom_blow_timer_value: The timer value of qfprom when doing efuse blow. + * @qfprom_blow_set_freq: The frequency required to set when we start the + * fuse blowing. + */ +struct qfprom_soc_data { + u32 accel_value; + u32 qfprom_blow_timer_value; + u32 qfprom_blow_set_freq; +}; + +/** + * struct qfprom_priv - structure holding qfprom attributes + * + * @qfpraw: iomapped memory space for qfprom-efuse raw address space. + * @qfpconf: iomapped memory space for qfprom-efuse configuration address + * space. + * @qfpcorrected: iomapped memory space for qfprom corrected address space. + * @qfpsecurity: iomapped memory space for qfprom security control space. + * @dev: qfprom device structure. + * @secclk: Clock supply. + * @vcc: Regulator supply. + * @soc_data: Data that for things that varies from SoC to SoC. + */ struct qfprom_priv { - void __iomem *base; + void __iomem *qfpraw; + void __iomem *qfpconf; + void __iomem *qfpcorrected; + void __iomem *qfpsecurity; + struct device *dev; + struct clk *secclk; + struct regulator *vcc; + const struct qfprom_soc_data *soc_data; +}; + +/** + * struct qfprom_touched_values - saved values to restore after blowing + * + * @clk_rate: The rate the clock was at before blowing. + * @accel_val: The value of the accel reg before blowing. + * @timer_val: The value of the timer before blowing. + */ +struct qfprom_touched_values { + unsigned long clk_rate; + u32 accel_val; + u32 timer_val; }; +/** + * qfprom_disable_fuse_blowing() - Undo enabling of fuse blowing. + * @priv: Our driver data. + * @old: The data that was stashed from before fuse blowing. + * + * Resets the value of the blow timer, accel register and the clock + * and voltage settings. + * + * Prints messages if there are errors but doesn't return an error code + * since there's not much we can do upon failure. + */ +static void qfprom_disable_fuse_blowing(const struct qfprom_priv *priv, + const struct qfprom_touched_values *old) +{ + int ret; + + ret = regulator_disable(priv->vcc); + if (ret) + dev_warn(priv->dev, "Failed to disable regulator (ignoring)\n"); + + ret = clk_set_rate(priv->secclk, old->clk_rate); + if (ret) + dev_warn(priv->dev, + "Failed to set clock rate for disable (ignoring)\n"); + + clk_disable_unprepare(priv->secclk); + + writel(old->timer_val, priv->qfpconf + QFPROM_BLOW_TIMER_OFFSET); + writel(old->accel_val, priv->qfpconf + QFPROM_ACCEL_OFFSET); +} + +/** + * qfprom_enable_fuse_blowing() - Enable fuse blowing. + * @priv: Our driver data. + * @old: We'll stash stuff here to use when disabling. + * + * Sets the value of the blow timer, accel register and the clock + * and voltage settings. + * + * Prints messages if there are errors so caller doesn't need to. + * + * Return: 0 or -err. + */ +static int qfprom_enable_fuse_blowing(const struct qfprom_priv *priv, + struct qfprom_touched_values *old) +{ + int ret; + + ret = clk_prepare_enable(priv->secclk); + if (ret) { + dev_err(priv->dev, "Failed to enable clock\n"); + return ret; + } + + old->clk_rate = clk_get_rate(priv->secclk); + ret = clk_set_rate(priv->secclk, priv->soc_data->qfprom_blow_set_freq); + if (ret) { + dev_err(priv->dev, "Failed to set clock rate for enable\n"); + goto err_clk_prepared; + } + + ret = regulator_enable(priv->vcc); + if (ret) { + dev_err(priv->dev, "Failed to enable regulator\n"); + goto err_clk_rate_set; + } + + old->timer_val = readl(priv->qfpconf + QFPROM_BLOW_TIMER_OFFSET); + old->accel_val = readl(priv->qfpconf + QFPROM_ACCEL_OFFSET); + writel(priv->soc_data->qfprom_blow_timer_value, + priv->qfpconf + QFPROM_BLOW_TIMER_OFFSET); + writel(priv->soc_data->accel_value, + priv->qfpconf + QFPROM_ACCEL_OFFSET); + + return 0; + +err_clk_rate_set: + clk_set_rate(priv->secclk, old->clk_rate); +err_clk_prepared: + clk_disable_unprepare(priv->secclk); + return ret; +} + +/** + * qfprom_efuse_reg_write() - Write to fuses. + * @context: Our driver data. + * @reg: The offset to write at. + * @_val: Pointer to data to write. + * @bytes: The number of bytes to write. + * + * Writes to fuses. WARNING: THIS IS PERMANENT. + * + * Return: 0 or -err. + */ +static int qfprom_reg_write(void *context, unsigned int reg, void *_val, + size_t bytes) +{ + struct qfprom_priv *priv = context; + struct qfprom_touched_values old; + int words = bytes / 4; + u32 *value = _val; + u32 blow_status; + int ret; + int i; + + dev_dbg(priv->dev, + "Writing to raw qfprom region : %#010x of size: %zu\n", + reg, bytes); + + /* + * The hardware only allows us to write word at a time, but we can + * read byte at a time. Until the nvmem framework allows a separate + * word_size and stride for reading vs. writing, we'll enforce here. + */ + if (bytes % 4) { + dev_err(priv->dev, + "%zu is not an integral number of words\n", bytes); + return -EINVAL; + } + if (reg % 4) { + dev_err(priv->dev, + "Invalid offset: %#x. Must be word aligned\n", reg); + return -EINVAL; + } + + ret = qfprom_enable_fuse_blowing(priv, &old); + if (ret) + return ret; + + ret = readl_relaxed_poll_timeout( + priv->qfpconf + QFPROM_BLOW_STATUS_OFFSET, + blow_status, blow_status == QFPROM_BLOW_STATUS_READY, + QFPROM_FUSE_BLOW_POLL_US, QFPROM_FUSE_BLOW_TIMEOUT_US); + + if (ret) { + dev_err(priv->dev, + "Timeout waiting for initial ready; aborting.\n"); + goto exit_enabled_fuse_blowing; + } + + for (i = 0; i < words; i++) + writel(value[i], priv->qfpraw + reg + (i * 4)); + + ret = readl_relaxed_poll_timeout( + priv->qfpconf + QFPROM_BLOW_STATUS_OFFSET, + blow_status, blow_status == QFPROM_BLOW_STATUS_READY, + QFPROM_FUSE_BLOW_POLL_US, QFPROM_FUSE_BLOW_TIMEOUT_US); + + /* Give an error, but not much we can do in this case */ + if (ret) + dev_err(priv->dev, "Timeout waiting for finish.\n"); + +exit_enabled_fuse_blowing: + qfprom_disable_fuse_blowing(priv, &old); + + return ret; +} + static int qfprom_reg_read(void *context, unsigned int reg, void *_val, size_t bytes) { struct qfprom_priv *priv = context; u8 *val = _val; int i = 0, words = bytes; + void __iomem *base = priv->qfpcorrected; + + if (read_raw_data && priv->qfpraw) + base = priv->qfpraw; while (words--) - *val++ = readb(priv->base + reg + i++); + *val++ = readb(base + reg + i++); return 0; } -static struct nvmem_config econfig = { - .name = "qfprom", - .stride = 1, - .word_size = 1, - .id = NVMEM_DEVID_AUTO, - .reg_read = qfprom_reg_read, +static const struct qfprom_soc_data qfprom_7_8_data = { + .accel_value = 0xD10, + .qfprom_blow_timer_value = 25, + .qfprom_blow_set_freq = 4800000, }; static int qfprom_probe(struct platform_device *pdev) { + struct nvmem_config econfig = { + .name = "qfprom", + .stride = 1, + .word_size = 1, + .id = NVMEM_DEVID_AUTO, + .reg_read = qfprom_reg_read, + }; struct device *dev = &pdev->dev; struct resource *res; struct nvmem_device *nvmem; struct qfprom_priv *priv; + int ret; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; + /* The corrected section is always provided */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - priv->base = devm_ioremap_resource(dev, res); - if (IS_ERR(priv->base)) - return PTR_ERR(priv->base); + priv->qfpcorrected = devm_ioremap_resource(dev, res); + if (IS_ERR(priv->qfpcorrected)) + return PTR_ERR(priv->qfpcorrected); econfig.size = resource_size(res); econfig.dev = dev; econfig.priv = priv; + priv->dev = dev; + + /* + * If more than one region is provided then the OS has the ability + * to write. + */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (res) { + u32 version; + int major_version, minor_version; + + priv->qfpraw = devm_ioremap_resource(dev, res); + if (IS_ERR(priv->qfpraw)) + return PTR_ERR(priv->qfpraw); + res = platform_get_resource(pdev, IORESOURCE_MEM, 2); + priv->qfpconf = devm_ioremap_resource(dev, res); + if (IS_ERR(priv->qfpconf)) + return PTR_ERR(priv->qfpconf); + res = platform_get_resource(pdev, IORESOURCE_MEM, 3); + priv->qfpsecurity = devm_ioremap_resource(dev, res); + if (IS_ERR(priv->qfpsecurity)) + return PTR_ERR(priv->qfpsecurity); + + version = readl(priv->qfpsecurity + QFPROM_VERSION_OFFSET); + major_version = (version & QFPROM_MAJOR_VERSION_MASK) >> + QFPROM_MAJOR_VERSION_SHIFT; + minor_version = (version & QFPROM_MINOR_VERSION_MASK) >> + QFPROM_MINOR_VERSION_SHIFT; + + if (major_version == 7 && minor_version == 8) + priv->soc_data = &qfprom_7_8_data; + + priv->vcc = devm_regulator_get(&pdev->dev, "vcc"); + if (IS_ERR(priv->vcc)) + return PTR_ERR(priv->vcc); + + priv->secclk = devm_clk_get(dev, "core"); + if (IS_ERR(priv->secclk)) { + ret = PTR_ERR(priv->secclk); + if (ret != -EPROBE_DEFER) + dev_err(dev, "Error getting clock: %d\n", ret); + return ret; + } + + /* Only enable writing if we have SoC data. */ + if (priv->soc_data) + econfig.reg_write = qfprom_reg_write; + } + nvmem = devm_nvmem_register(dev, &econfig); return PTR_ERR_OR_ZERO(nvmem); -- cgit v1.2.3-59-g8ed1b From b7194ba1e19fe029e7f6cc2db017a3001dc03086 Mon Sep 17 00:00:00 2001 From: Matteo Croce Date: Wed, 22 Jul 2020 11:07:03 +0100 Subject: nvmem: update Kconfig description nvmem can't be built as module anymore, update its Kconfig description. Fixes: 2a37ce25d9f2 ("nvmem: disallow modular CONFIG_NVMEM") Signed-off-by: Matteo Croce Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20200722100705.7772-13-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/Kconfig | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig index d7b7f6d688e7..954d3b4a52ab 100644 --- a/drivers/nvmem/Kconfig +++ b/drivers/nvmem/Kconfig @@ -7,9 +7,6 @@ menuconfig NVMEM This framework is designed to provide a generic interface to NVMEM from both the Linux Kernel and the userspace. - This driver can also be built as a module. If so, the module - will be called nvmem_core. - If unsure, say no. if NVMEM -- cgit v1.2.3-59-g8ed1b From fa2dc6049afbd90301de5c26e0626dc695000589 Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Wed, 22 Jul 2020 11:07:04 +0100 Subject: dt-bindings: nvmem: SID: add binding for A100's SID controller Add a binding for A100's SID controller. Signed-off-by: Yangtao Li Reviewed-by: Rob Herring Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20200722100705.7772-14-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- .../bindings/nvmem/allwinner,sun4i-a10-sid.yaml | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/Documentation/devicetree/bindings/nvmem/allwinner,sun4i-a10-sid.yaml b/Documentation/devicetree/bindings/nvmem/allwinner,sun4i-a10-sid.yaml index daf1321d76ad..6687ab720304 100644 --- a/Documentation/devicetree/bindings/nvmem/allwinner,sun4i-a10-sid.yaml +++ b/Documentation/devicetree/bindings/nvmem/allwinner,sun4i-a10-sid.yaml @@ -15,14 +15,17 @@ allOf: properties: compatible: - enum: - - allwinner,sun4i-a10-sid - - allwinner,sun7i-a20-sid - - allwinner,sun8i-a83t-sid - - allwinner,sun8i-h3-sid - - allwinner,sun50i-a64-sid - - allwinner,sun50i-h5-sid - - allwinner,sun50i-h6-sid + oneOf: + - const: allwinner,sun4i-a10-sid + - const: allwinner,sun7i-a20-sid + - const: allwinner,sun8i-a83t-sid + - const: allwinner,sun8i-h3-sid + - const: allwinner,sun50i-a64-sid + - items: + - const: allwinner,sun50i-a100-sid + - const: allwinner,sun50i-a64-sid + - const: allwinner,sun50i-h5-sid + - const: allwinner,sun50i-h6-sid reg: maxItems: 1 -- cgit v1.2.3-59-g8ed1b From 8f042191d430f4144d23ebc2206e8cf73e6b1cbf Mon Sep 17 00:00:00 2001 From: Guru Das Srinagesh Date: Wed, 22 Jul 2020 11:07:05 +0100 Subject: nvmem: qcom-spmi-sdam: Enable multiple devices Using pdev->id as the nvmem's config ID (which, by default, is NVMEM_DEVID_NONE) prevents multiple instances of this driver from probing because of the following error: sysfs: cannot create duplicate filename '/bus/nvmem/devices/spmi_sdam' Use NVMEM_DEVID_AUTO as the NVMEM config ID to fix the issue. Signed-off-by: Guru Das Srinagesh Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20200722100705.7772-15-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/qcom-spmi-sdam.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/nvmem/qcom-spmi-sdam.c b/drivers/nvmem/qcom-spmi-sdam.c index 8682cda448d6..a72704cd0468 100644 --- a/drivers/nvmem/qcom-spmi-sdam.c +++ b/drivers/nvmem/qcom-spmi-sdam.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2017 The Linux Foundation. All rights reserved. + * Copyright (c) 2017, 2020 The Linux Foundation. All rights reserved. */ #include @@ -141,7 +141,7 @@ static int sdam_probe(struct platform_device *pdev) sdam->sdam_config.dev = &pdev->dev; sdam->sdam_config.name = "spmi_sdam"; - sdam->sdam_config.id = pdev->id; + sdam->sdam_config.id = NVMEM_DEVID_AUTO; sdam->sdam_config.owner = THIS_MODULE, sdam->sdam_config.stride = 1; sdam->sdam_config.word_size = 1; -- cgit v1.2.3-59-g8ed1b From 22362aa30bad6f03b5bcbbeee3cdc61950d40086 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 29 Jul 2020 23:59:02 +0800 Subject: habanalabs: remove unused but set variable 'ctx_asid' Gcc report warning as follows: drivers/misc/habanalabs/common/command_submission.c:373:6: warning: variable 'ctx_asid' set but not used [-Wunused-but-set-variable] 373 | int ctx_asid, rc; | ^~~~~~~~ This variable is not used in function cs_timedout(), this commit remove it to fix the warning. Reported-by: Hulk Robot Signed-off-by: Wei Yongjun Link: https://lore.kernel.org/r/20200729155902.33976-1-weiyongjun1@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/habanalabs/common/command_submission.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/misc/habanalabs/common/command_submission.c b/drivers/misc/habanalabs/common/command_submission.c index e096532c0e48..b9840e368eb5 100644 --- a/drivers/misc/habanalabs/common/command_submission.c +++ b/drivers/misc/habanalabs/common/command_submission.c @@ -370,7 +370,7 @@ static void cs_do_release(struct kref *ref) static void cs_timedout(struct work_struct *work) { struct hl_device *hdev; - int ctx_asid, rc; + int rc; struct hl_cs *cs = container_of(work, struct hl_cs, work_tdr.work); rc = cs_get_unless_zero(cs); @@ -386,7 +386,6 @@ static void cs_timedout(struct work_struct *work) cs->timedout = true; hdev = cs->ctx->hdev; - ctx_asid = cs->ctx->asid; dev_err(hdev->dev, "Command submission %llu has not finished in time!\n", -- cgit v1.2.3-59-g8ed1b