]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
Merge tag 'qcom-drivers-for-4.20' of git://git.kernel.org/pub/scm/linux/kernel/git...
authorArnd Bergmann <arnd@arndb.de>
Tue, 2 Oct 2018 08:11:05 +0000 (10:11 +0200)
committerArnd Bergmann <arnd@arndb.de>
Tue, 2 Oct 2018 08:11:12 +0000 (10:11 +0200)
Qualcomm ARM Based Driver Updates for v4.20

* Refactor of SCM compatibles and clock requirements
* SMEM cleanup
* Add LLCC EDAC driver
* Fixes for GENI clocks and macros
* Fix includes for llcc-slice and smem
* String overflow fixes for APR and wcnss_ctrl
* Fixup for COMPILE_TEST of qcom driver Kconfigs
* Cleanup of Kconfig depends of rpmh, smd_rpm, smsm, and smp2p
* Add SCM dependencies to SPM and rmtfs-mem

* tag 'qcom-drivers-for-4.20' of git://git.kernel.org/pub/scm/linux/kernel/git/agross/linux: (38 commits)
  soc: qcom: geni: geni_se_clk_freq_match() should always accept multiples
  soc: qcom: geni: Don't ignore clk_round_rate() errors in geni_se_clk_tbl_get()
  soc: qcom: geni: Make version macros simpler
  dt-bindings: firmware: scm: Add MSM8998 and SDM845
  firmware: qcom: scm: Refactor clock handling
  dt-bindings: firmware: scm: Refactor compatibles and clocks
  soc: qcom: smem: a few last cleanups
  soc: qcom: smem: verify partition host ids match
  soc: qcom: smem: small change in global entry loop
  soc: qcom: smem: verify partition offset_free_uncached
  soc: qcom: smem: verify partition header size
  soc: qcom: smem: introduce qcom_smem_partition_header()
  soc: qcom: smem: require order of host ids to match
  soc: qcom: smem: verify both host ids in partition header
  soc: qcom: smem: small refactor in qcom_smem_enumerate_partitions()
  soc: qcom: smem: always ignore partitions with 0 offset or size
  soc: qcom: smem: initialize region struct only when successful
  soc: qcom: smem: rename variable in qcom_smem_get_global()
  drivers: qcom: rpmh-rsc: clear wait_for_compl after use
  soc: qcom: rmtfs-mem: Validate that scm is available
  ...

Signed-off-by: Arnd Bergmann <arnd@arndb.de>
18 files changed:
Documentation/devicetree/bindings/arm/msm/qcom,llcc.txt
Documentation/devicetree/bindings/firmware/qcom,scm.txt
MAINTAINERS
drivers/edac/Kconfig
drivers/edac/Makefile
drivers/edac/qcom_edac.c [new file with mode: 0644]
drivers/firmware/qcom_scm.c
drivers/soc/qcom/Kconfig
drivers/soc/qcom/apr.c
drivers/soc/qcom/llcc-slice.c
drivers/soc/qcom/qcom-geni-se.c
drivers/soc/qcom/rmtfs_mem.c
drivers/soc/qcom/rpmh-rsc.c
drivers/soc/qcom/smem.c
drivers/soc/qcom/spm.c
drivers/soc/qcom/wcnss_ctrl.c
include/linux/qcom-geni-se.h
include/linux/soc/qcom/llcc-qcom.h

index 5e85749262aee881f720be668b0ad8bd63e54f52..eaee06b2d8f2346829d8d25ddc67658b336f4772 100644 (file)
@@ -16,11 +16,26 @@ Properties:
 - reg:
        Usage: required
        Value Type: <prop-encoded-array>
-       Definition: Start address and the the size of the register region.
+       Definition: The first element specifies the llcc base start address and
+                   the size of the register region. The second element specifies
+                   the llcc broadcast base address and size of the register region.
+
+- reg-names:
+        Usage: required
+        Value Type: <stringlist>
+        Definition: Register region names. Must be "llcc_base", "llcc_broadcast_base".
+
+- interrupts:
+       Usage: required
+       Definition: The interrupt is associated with the llcc edac device.
+                       It's used for llcc cache single and double bit error detection
+                       and reporting.
 
 Example:
 
        cache-controller@1100000 {
                compatible = "qcom,sdm845-llcc";
-               reg = <0x1100000 0x250000>;
+               reg = <0x1100000 0x200000>, <0x1300000 0x50000> ;
+               reg-names = "llcc_base", "llcc_broadcast_base";
+               interrupts = <GIC_SPI 582 IRQ_TYPE_LEVEL_HIGH>;
        };
index fcf6979c0b6d3e0ba95f22ea70e56d103ac77d1e..41f133a4e2fa72014b37083437fdf99b09710172 100644 (file)
@@ -7,16 +7,23 @@ assorted actions.
 
 Required properties:
 - compatible: must contain one of the following:
- * "qcom,scm-apq8064" for APQ8064 platforms
- * "qcom,scm-msm8660" for MSM8660 platforms
- * "qcom,scm-msm8690" for MSM8690 platforms
- * "qcom,scm-msm8996" for MSM8996 platforms
- * "qcom,scm-ipq4019" for IPQ4019 platforms
- * "qcom,scm" for later processors (MSM8916, APQ8084, MSM8974, etc)
-- clocks: One to three clocks may be required based on compatible.
- * No clock required for "qcom,scm-msm8996", "qcom,scm-ipq4019"
- * Only core clock required for "qcom,scm-apq8064", "qcom,scm-msm8660", and "qcom,scm-msm8960"
- * Core, iface, and bus clocks required for "qcom,scm"
+ * "qcom,scm-apq8064"
+ * "qcom,scm-apq8084"
+ * "qcom,scm-msm8660"
+ * "qcom,scm-msm8916"
+ * "qcom,scm-msm8960"
+ * "qcom,scm-msm8974"
+ * "qcom,scm-msm8996"
+ * "qcom,scm-msm8998"
+ * "qcom,scm-ipq4019"
+ * "qcom,scm-sdm845"
+ and:
+ * "qcom,scm"
+- clocks: Specifies clocks needed by the SCM interface, if any:
+ * core clock required for "qcom,scm-apq8064", "qcom,scm-msm8660" and
+   "qcom,scm-msm8960"
+ * core, iface and bus clocks required for "qcom,scm-apq8084",
+   "qcom,scm-msm8916" and "qcom,scm-msm8974"
 - clock-names: Must contain "core" for the core clock, "iface" for the interface
   clock and "bus" for the bus clock per the requirements of the compatible.
 - qcom,dload-mode: phandle to the TCSR hardware block and offset of the
@@ -26,8 +33,10 @@ Example for MSM8916:
 
        firmware {
                scm {
-                       compatible = "qcom,scm";
-                       clocks = <&gcc GCC_CRYPTO_CLK> , <&gcc GCC_CRYPTO_AXI_CLK>, <&gcc GCC_CRYPTO_AHB_CLK>;
+                       compatible = "qcom,msm8916", "qcom,scm";
+                       clocks = <&gcc GCC_CRYPTO_CLK> ,
+                                <&gcc GCC_CRYPTO_AXI_CLK>,
+                                <&gcc GCC_CRYPTO_AHB_CLK>;
                        clock-names = "core", "bus", "iface";
                };
        };
index d870cb57c887a1c5fa773a8fd9d614c54cb3cce0..9e536acfd5f82dd81996ac7337f0a688fe14f2b8 100644 (file)
@@ -5347,6 +5347,14 @@ L:       linux-edac@vger.kernel.org
 S:     Maintained
 F:     drivers/edac/ti_edac.c
 
+EDAC-QCOM
+M:     Channagoud Kadabi <ckadabi@codeaurora.org>
+M:     Venkata Narendra Kumar Gutta <vnkgutta@codeaurora.org>
+L:     linux-arm-msm@vger.kernel.org
+L:     linux-edac@vger.kernel.org
+S:     Maintained
+F:     drivers/edac/qcom_edac.c
+
 EDIROL UA-101/UA-1000 DRIVER
 M:     Clemens Ladisch <clemens@ladisch.de>
 L:     alsa-devel@alsa-project.org (moderated for non-subscribers)
index 57304b2e989f2ca6ba45fe808cb933240273e7a0..df9467eef32a0e4b67090e56e1a5c0260a0f4037 100644 (file)
@@ -460,4 +460,18 @@ config EDAC_TI
          Support for error detection and correction on the
           TI SoCs.
 
+config EDAC_QCOM
+       tristate "QCOM EDAC Controller"
+       depends on ARCH_QCOM && QCOM_LLCC
+       help
+         Support for error detection and correction on the
+         Qualcomm Technologies, Inc. SoCs.
+
+         This driver reports Single Bit Errors (SBEs) and Double Bit Errors (DBEs).
+         As of now, it supports error reporting for Last Level Cache Controller (LLCC)
+         of Tag RAM and Data RAM.
+
+         For debugging issues having to do with stability and overall system
+         health, you should probably say 'Y' here.
+
 endif # EDAC
index 02b43a7d8c3ee3072863c8560d08eb871962feb7..716096d08ea00c6ee4a304c9adb88ca60752691d 100644 (file)
@@ -77,3 +77,4 @@ obj-$(CONFIG_EDAC_ALTERA)             += altera_edac.o
 obj-$(CONFIG_EDAC_SYNOPSYS)            += synopsys_edac.o
 obj-$(CONFIG_EDAC_XGENE)               += xgene_edac.o
 obj-$(CONFIG_EDAC_TI)                  += ti_edac.o
+obj-$(CONFIG_EDAC_QCOM)                        += qcom_edac.o
diff --git a/drivers/edac/qcom_edac.c b/drivers/edac/qcom_edac.c
new file mode 100644 (file)
index 0000000..82bd775
--- /dev/null
@@ -0,0 +1,414 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/edac.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/soc/qcom/llcc-qcom.h>
+
+#include "edac_mc.h"
+#include "edac_device.h"
+
+#define EDAC_LLCC                       "qcom_llcc"
+
+#define LLCC_ERP_PANIC_ON_UE            1
+
+#define TRP_SYN_REG_CNT                 6
+#define DRP_SYN_REG_CNT                 8
+
+#define LLCC_COMMON_STATUS0             0x0003000c
+#define LLCC_LB_CNT_MASK                GENMASK(31, 28)
+#define LLCC_LB_CNT_SHIFT               28
+
+/* Single & double bit syndrome register offsets */
+#define TRP_ECC_SB_ERR_SYN0             0x0002304c
+#define TRP_ECC_DB_ERR_SYN0             0x00020370
+#define DRP_ECC_SB_ERR_SYN0             0x0004204c
+#define DRP_ECC_DB_ERR_SYN0             0x00042070
+
+/* Error register offsets */
+#define TRP_ECC_ERROR_STATUS1           0x00020348
+#define TRP_ECC_ERROR_STATUS0           0x00020344
+#define DRP_ECC_ERROR_STATUS1           0x00042048
+#define DRP_ECC_ERROR_STATUS0           0x00042044
+
+/* TRP, DRP interrupt register offsets */
+#define DRP_INTERRUPT_STATUS            0x00041000
+#define TRP_INTERRUPT_0_STATUS          0x00020480
+#define DRP_INTERRUPT_CLEAR             0x00041008
+#define DRP_ECC_ERROR_CNTR_CLEAR        0x00040004
+#define TRP_INTERRUPT_0_CLEAR           0x00020484
+#define TRP_ECC_ERROR_CNTR_CLEAR        0x00020440
+
+/* Mask and shift macros */
+#define ECC_DB_ERR_COUNT_MASK           GENMASK(4, 0)
+#define ECC_DB_ERR_WAYS_MASK            GENMASK(31, 16)
+#define ECC_DB_ERR_WAYS_SHIFT           BIT(4)
+
+#define ECC_SB_ERR_COUNT_MASK           GENMASK(23, 16)
+#define ECC_SB_ERR_COUNT_SHIFT          BIT(4)
+#define ECC_SB_ERR_WAYS_MASK            GENMASK(15, 0)
+
+#define SB_ECC_ERROR                    BIT(0)
+#define DB_ECC_ERROR                    BIT(1)
+
+#define DRP_TRP_INT_CLEAR               GENMASK(1, 0)
+#define DRP_TRP_CNT_CLEAR               GENMASK(1, 0)
+
+/* Config registers offsets*/
+#define DRP_ECC_ERROR_CFG               0x00040000
+
+/* Tag RAM, Data RAM interrupt register offsets */
+#define CMN_INTERRUPT_0_ENABLE          0x0003001c
+#define CMN_INTERRUPT_2_ENABLE          0x0003003c
+#define TRP_INTERRUPT_0_ENABLE          0x00020488
+#define DRP_INTERRUPT_ENABLE            0x0004100c
+
+#define SB_ERROR_THRESHOLD              0x1
+#define SB_ERROR_THRESHOLD_SHIFT        24
+#define SB_DB_TRP_INTERRUPT_ENABLE      0x3
+#define TRP0_INTERRUPT_ENABLE           0x1
+#define DRP0_INTERRUPT_ENABLE           BIT(6)
+#define SB_DB_DRP_INTERRUPT_ENABLE      0x3
+
+enum {
+       LLCC_DRAM_CE = 0,
+       LLCC_DRAM_UE,
+       LLCC_TRAM_CE,
+       LLCC_TRAM_UE,
+};
+
+static const struct llcc_edac_reg_data edac_reg_data[] = {
+       [LLCC_DRAM_CE] = {
+               .name = "DRAM Single-bit",
+               .synd_reg = DRP_ECC_SB_ERR_SYN0,
+               .count_status_reg = DRP_ECC_ERROR_STATUS1,
+               .ways_status_reg = DRP_ECC_ERROR_STATUS0,
+               .reg_cnt = DRP_SYN_REG_CNT,
+               .count_mask = ECC_SB_ERR_COUNT_MASK,
+               .ways_mask = ECC_SB_ERR_WAYS_MASK,
+               .count_shift = ECC_SB_ERR_COUNT_SHIFT,
+       },
+       [LLCC_DRAM_UE] = {
+               .name = "DRAM Double-bit",
+               .synd_reg = DRP_ECC_DB_ERR_SYN0,
+               .count_status_reg = DRP_ECC_ERROR_STATUS1,
+               .ways_status_reg = DRP_ECC_ERROR_STATUS0,
+               .reg_cnt = DRP_SYN_REG_CNT,
+               .count_mask = ECC_DB_ERR_COUNT_MASK,
+               .ways_mask = ECC_DB_ERR_WAYS_MASK,
+               .ways_shift = ECC_DB_ERR_WAYS_SHIFT,
+       },
+       [LLCC_TRAM_CE] = {
+               .name = "TRAM Single-bit",
+               .synd_reg = TRP_ECC_SB_ERR_SYN0,
+               .count_status_reg = TRP_ECC_ERROR_STATUS1,
+               .ways_status_reg = TRP_ECC_ERROR_STATUS0,
+               .reg_cnt = TRP_SYN_REG_CNT,
+               .count_mask = ECC_SB_ERR_COUNT_MASK,
+               .ways_mask = ECC_SB_ERR_WAYS_MASK,
+               .count_shift = ECC_SB_ERR_COUNT_SHIFT,
+       },
+       [LLCC_TRAM_UE] = {
+               .name = "TRAM Double-bit",
+               .synd_reg = TRP_ECC_DB_ERR_SYN0,
+               .count_status_reg = TRP_ECC_ERROR_STATUS1,
+               .ways_status_reg = TRP_ECC_ERROR_STATUS0,
+               .reg_cnt = TRP_SYN_REG_CNT,
+               .count_mask = ECC_DB_ERR_COUNT_MASK,
+               .ways_mask = ECC_DB_ERR_WAYS_MASK,
+               .ways_shift = ECC_DB_ERR_WAYS_SHIFT,
+       },
+};
+
+static int qcom_llcc_core_setup(struct regmap *llcc_bcast_regmap)
+{
+       u32 sb_err_threshold;
+       int ret;
+
+       /*
+        * Configure interrupt enable registers such that Tag, Data RAM related
+        * interrupts are propagated to interrupt controller for servicing
+        */
+       ret = regmap_update_bits(llcc_bcast_regmap, CMN_INTERRUPT_2_ENABLE,
+                                TRP0_INTERRUPT_ENABLE,
+                                TRP0_INTERRUPT_ENABLE);
+       if (ret)
+               return ret;
+
+       ret = regmap_update_bits(llcc_bcast_regmap, TRP_INTERRUPT_0_ENABLE,
+                                SB_DB_TRP_INTERRUPT_ENABLE,
+                                SB_DB_TRP_INTERRUPT_ENABLE);
+       if (ret)
+               return ret;
+
+       sb_err_threshold = (SB_ERROR_THRESHOLD << SB_ERROR_THRESHOLD_SHIFT);
+       ret = regmap_write(llcc_bcast_regmap, DRP_ECC_ERROR_CFG,
+                          sb_err_threshold);
+       if (ret)
+               return ret;
+
+       ret = regmap_update_bits(llcc_bcast_regmap, CMN_INTERRUPT_2_ENABLE,
+                                DRP0_INTERRUPT_ENABLE,
+                                DRP0_INTERRUPT_ENABLE);
+       if (ret)
+               return ret;
+
+       ret = regmap_write(llcc_bcast_regmap, DRP_INTERRUPT_ENABLE,
+                          SB_DB_DRP_INTERRUPT_ENABLE);
+       return ret;
+}
+
+/* Clear the error interrupt and counter registers */
+static int
+qcom_llcc_clear_error_status(int err_type, struct llcc_drv_data *drv)
+{
+       int ret = 0;
+
+       switch (err_type) {
+       case LLCC_DRAM_CE:
+       case LLCC_DRAM_UE:
+               ret = regmap_write(drv->bcast_regmap, DRP_INTERRUPT_CLEAR,
+                                  DRP_TRP_INT_CLEAR);
+               if (ret)
+                       return ret;
+
+               ret = regmap_write(drv->bcast_regmap, DRP_ECC_ERROR_CNTR_CLEAR,
+                                  DRP_TRP_CNT_CLEAR);
+               if (ret)
+                       return ret;
+               break;
+       case LLCC_TRAM_CE:
+       case LLCC_TRAM_UE:
+               ret = regmap_write(drv->bcast_regmap, TRP_INTERRUPT_0_CLEAR,
+                                  DRP_TRP_INT_CLEAR);
+               if (ret)
+                       return ret;
+
+               ret = regmap_write(drv->bcast_regmap, TRP_ECC_ERROR_CNTR_CLEAR,
+                                  DRP_TRP_CNT_CLEAR);
+               if (ret)
+                       return ret;
+               break;
+       default:
+               ret = -EINVAL;
+               edac_printk(KERN_CRIT, EDAC_LLCC, "Unexpected error type: %d\n",
+                           err_type);
+       }
+       return ret;
+}
+
+/* Dump Syndrome registers data for Tag RAM, Data RAM bit errors*/
+static int
+dump_syn_reg_values(struct llcc_drv_data *drv, u32 bank, int err_type)
+{
+       struct llcc_edac_reg_data reg_data = edac_reg_data[err_type];
+       int err_cnt, err_ways, ret, i;
+       u32 synd_reg, synd_val;
+
+       for (i = 0; i < reg_data.reg_cnt; i++) {
+               synd_reg = reg_data.synd_reg + (i * 4);
+               ret = regmap_read(drv->regmap, drv->offsets[bank] + synd_reg,
+                                 &synd_val);
+               if (ret)
+                       goto clear;
+
+               edac_printk(KERN_CRIT, EDAC_LLCC, "%s: ECC_SYN%d: 0x%8x\n",
+                           reg_data.name, i, synd_val);
+       }
+
+       ret = regmap_read(drv->regmap,
+                         drv->offsets[bank] + reg_data.count_status_reg,
+                         &err_cnt);
+       if (ret)
+               goto clear;
+
+       err_cnt &= reg_data.count_mask;
+       err_cnt >>= reg_data.count_shift;
+       edac_printk(KERN_CRIT, EDAC_LLCC, "%s: Error count: 0x%4x\n",
+                   reg_data.name, err_cnt);
+
+       ret = regmap_read(drv->regmap,
+                         drv->offsets[bank] + reg_data.ways_status_reg,
+                         &err_ways);
+       if (ret)
+               goto clear;
+
+       err_ways &= reg_data.ways_mask;
+       err_ways >>= reg_data.ways_shift;
+
+       edac_printk(KERN_CRIT, EDAC_LLCC, "%s: Error ways: 0x%4x\n",
+                   reg_data.name, err_ways);
+
+clear:
+       return qcom_llcc_clear_error_status(err_type, drv);
+}
+
+static int
+dump_syn_reg(struct edac_device_ctl_info *edev_ctl, int err_type, u32 bank)
+{
+       struct llcc_drv_data *drv = edev_ctl->pvt_info;
+       int ret;
+
+       ret = dump_syn_reg_values(drv, bank, err_type);
+       if (ret)
+               return ret;
+
+       switch (err_type) {
+       case LLCC_DRAM_CE:
+               edac_device_handle_ce(edev_ctl, 0, bank,
+                                     "LLCC Data RAM correctable Error");
+               break;
+       case LLCC_DRAM_UE:
+               edac_device_handle_ue(edev_ctl, 0, bank,
+                                     "LLCC Data RAM uncorrectable Error");
+               break;
+       case LLCC_TRAM_CE:
+               edac_device_handle_ce(edev_ctl, 0, bank,
+                                     "LLCC Tag RAM correctable Error");
+               break;
+       case LLCC_TRAM_UE:
+               edac_device_handle_ue(edev_ctl, 0, bank,
+                                     "LLCC Tag RAM uncorrectable Error");
+               break;
+       default:
+               ret = -EINVAL;
+               edac_printk(KERN_CRIT, EDAC_LLCC, "Unexpected error type: %d\n",
+                           err_type);
+       }
+
+       return ret;
+}
+
+static irqreturn_t
+llcc_ecc_irq_handler(int irq, void *edev_ctl)
+{
+       struct edac_device_ctl_info *edac_dev_ctl = edev_ctl;
+       struct llcc_drv_data *drv = edac_dev_ctl->pvt_info;
+       irqreturn_t irq_rc = IRQ_NONE;
+       u32 drp_error, trp_error, i;
+       bool irq_handled;
+       int ret;
+
+       /* Iterate over the banks and look for Tag RAM or Data RAM errors */
+       for (i = 0; i < drv->num_banks; i++) {
+               ret = regmap_read(drv->regmap,
+                                 drv->offsets[i] + DRP_INTERRUPT_STATUS,
+                                 &drp_error);
+
+               if (!ret && (drp_error & SB_ECC_ERROR)) {
+                       edac_printk(KERN_CRIT, EDAC_LLCC,
+                                   "Single Bit Error detected in Data RAM\n");
+                       ret = dump_syn_reg(edev_ctl, LLCC_DRAM_CE, i);
+               } else if (!ret && (drp_error & DB_ECC_ERROR)) {
+                       edac_printk(KERN_CRIT, EDAC_LLCC,
+                                   "Double Bit Error detected in Data RAM\n");
+                       ret = dump_syn_reg(edev_ctl, LLCC_DRAM_UE, i);
+               }
+               if (!ret)
+                       irq_handled = true;
+
+               ret = regmap_read(drv->regmap,
+                                 drv->offsets[i] + TRP_INTERRUPT_0_STATUS,
+                                 &trp_error);
+
+               if (!ret && (trp_error & SB_ECC_ERROR)) {
+                       edac_printk(KERN_CRIT, EDAC_LLCC,
+                                   "Single Bit Error detected in Tag RAM\n");
+                       ret = dump_syn_reg(edev_ctl, LLCC_TRAM_CE, i);
+               } else if (!ret && (trp_error & DB_ECC_ERROR)) {
+                       edac_printk(KERN_CRIT, EDAC_LLCC,
+                                   "Double Bit Error detected in Tag RAM\n");
+                       ret = dump_syn_reg(edev_ctl, LLCC_TRAM_UE, i);
+               }
+               if (!ret)
+                       irq_handled = true;
+       }
+
+       if (irq_handled)
+               irq_rc = IRQ_HANDLED;
+
+       return irq_rc;
+}
+
+static int qcom_llcc_edac_probe(struct platform_device *pdev)
+{
+       struct llcc_drv_data *llcc_driv_data = pdev->dev.platform_data;
+       struct edac_device_ctl_info *edev_ctl;
+       struct device *dev = &pdev->dev;
+       int ecc_irq;
+       int rc;
+
+       rc = qcom_llcc_core_setup(llcc_driv_data->bcast_regmap);
+       if (rc)
+               return rc;
+
+       /* Allocate edac control info */
+       edev_ctl = edac_device_alloc_ctl_info(0, "qcom-llcc", 1, "bank",
+                                             llcc_driv_data->num_banks, 1,
+                                             NULL, 0,
+                                             edac_device_alloc_index());
+
+       if (!edev_ctl)
+               return -ENOMEM;
+
+       edev_ctl->dev = dev;
+       edev_ctl->mod_name = dev_name(dev);
+       edev_ctl->dev_name = dev_name(dev);
+       edev_ctl->ctl_name = "llcc";
+       edev_ctl->panic_on_ue = LLCC_ERP_PANIC_ON_UE;
+       edev_ctl->pvt_info = llcc_driv_data;
+
+       rc = edac_device_add_device(edev_ctl);
+       if (rc)
+               goto out_mem;
+
+       platform_set_drvdata(pdev, edev_ctl);
+
+       /* Request for ecc irq */
+       ecc_irq = llcc_driv_data->ecc_irq;
+       if (ecc_irq < 0) {
+               rc = -ENODEV;
+               goto out_dev;
+       }
+       rc = devm_request_irq(dev, ecc_irq, llcc_ecc_irq_handler,
+                             IRQF_TRIGGER_HIGH, "llcc_ecc", edev_ctl);
+       if (rc)
+               goto out_dev;
+
+       return rc;
+
+out_dev:
+       edac_device_del_device(edev_ctl->dev);
+out_mem:
+       edac_device_free_ctl_info(edev_ctl);
+
+       return rc;
+}
+
+static int qcom_llcc_edac_remove(struct platform_device *pdev)
+{
+       struct edac_device_ctl_info *edev_ctl = dev_get_drvdata(&pdev->dev);
+
+       edac_device_del_device(edev_ctl->dev);
+       edac_device_free_ctl_info(edev_ctl);
+
+       return 0;
+}
+
+static struct platform_driver qcom_llcc_edac_driver = {
+       .probe = qcom_llcc_edac_probe,
+       .remove = qcom_llcc_edac_remove,
+       .driver = {
+               .name = "qcom_llcc_edac",
+       },
+};
+module_platform_driver(qcom_llcc_edac_driver);
+
+MODULE_DESCRIPTION("QCOM EDAC driver");
+MODULE_LICENSE("GPL v2");
index e778af766fae3c2c88d20e8f7ae6f47f9114935c..af4eee86919d86418245fd8e66fd47ed423dee13 100644 (file)
@@ -525,34 +525,44 @@ static int qcom_scm_probe(struct platform_device *pdev)
                return ret;
 
        clks = (unsigned long)of_device_get_match_data(&pdev->dev);
-       if (clks & SCM_HAS_CORE_CLK) {
-               scm->core_clk = devm_clk_get(&pdev->dev, "core");
-               if (IS_ERR(scm->core_clk)) {
-                       if (PTR_ERR(scm->core_clk) != -EPROBE_DEFER)
-                               dev_err(&pdev->dev,
-                                       "failed to acquire core clk\n");
+
+       scm->core_clk = devm_clk_get(&pdev->dev, "core");
+       if (IS_ERR(scm->core_clk)) {
+               if (PTR_ERR(scm->core_clk) == -EPROBE_DEFER)
+                       return PTR_ERR(scm->core_clk);
+
+               if (clks & SCM_HAS_CORE_CLK) {
+                       dev_err(&pdev->dev, "failed to acquire core clk\n");
                        return PTR_ERR(scm->core_clk);
                }
+
+               scm->core_clk = NULL;
        }
 
-       if (clks & SCM_HAS_IFACE_CLK) {
-               scm->iface_clk = devm_clk_get(&pdev->dev, "iface");
-               if (IS_ERR(scm->iface_clk)) {
-                       if (PTR_ERR(scm->iface_clk) != -EPROBE_DEFER)
-                               dev_err(&pdev->dev,
-                                       "failed to acquire iface clk\n");
+       scm->iface_clk = devm_clk_get(&pdev->dev, "iface");
+       if (IS_ERR(scm->iface_clk)) {
+               if (PTR_ERR(scm->iface_clk) == -EPROBE_DEFER)
+                       return PTR_ERR(scm->iface_clk);
+
+               if (clks & SCM_HAS_IFACE_CLK) {
+                       dev_err(&pdev->dev, "failed to acquire iface clk\n");
                        return PTR_ERR(scm->iface_clk);
                }
+
+               scm->iface_clk = NULL;
        }
 
-       if (clks & SCM_HAS_BUS_CLK) {
-               scm->bus_clk = devm_clk_get(&pdev->dev, "bus");
-               if (IS_ERR(scm->bus_clk)) {
-                       if (PTR_ERR(scm->bus_clk) != -EPROBE_DEFER)
-                               dev_err(&pdev->dev,
-                                       "failed to acquire bus clk\n");
+       scm->bus_clk = devm_clk_get(&pdev->dev, "bus");
+       if (IS_ERR(scm->bus_clk)) {
+               if (PTR_ERR(scm->bus_clk) == -EPROBE_DEFER)
+                       return PTR_ERR(scm->bus_clk);
+
+               if (clks & SCM_HAS_BUS_CLK) {
+                       dev_err(&pdev->dev, "failed to acquire bus clk\n");
                        return PTR_ERR(scm->bus_clk);
                }
+
+               scm->bus_clk = NULL;
        }
 
        scm->reset.ops = &qcom_scm_pas_reset_ops;
@@ -594,23 +604,23 @@ static const struct of_device_id qcom_scm_dt_match[] = {
        { .compatible = "qcom,scm-apq8064",
          /* FIXME: This should have .data = (void *) SCM_HAS_CORE_CLK */
        },
-       { .compatible = "qcom,scm-msm8660",
-         .data = (void *) SCM_HAS_CORE_CLK,
-       },
-       { .compatible = "qcom,scm-msm8960",
-         .data = (void *) SCM_HAS_CORE_CLK,
-       },
-       { .compatible = "qcom,scm-msm8996",
-         .data = NULL, /* no clocks */
+       { .compatible = "qcom,scm-apq8084", .data = (void *)(SCM_HAS_CORE_CLK |
+                                                            SCM_HAS_IFACE_CLK |
+                                                            SCM_HAS_BUS_CLK)
        },
-       { .compatible = "qcom,scm-ipq4019",
-         .data = NULL, /* no clocks */
+       { .compatible = "qcom,scm-ipq4019" },
+       { .compatible = "qcom,scm-msm8660", .data = (void *) SCM_HAS_CORE_CLK },
+       { .compatible = "qcom,scm-msm8960", .data = (void *) SCM_HAS_CORE_CLK },
+       { .compatible = "qcom,scm-msm8916", .data = (void *)(SCM_HAS_CORE_CLK |
+                                                            SCM_HAS_IFACE_CLK |
+                                                            SCM_HAS_BUS_CLK)
        },
-       { .compatible = "qcom,scm",
-         .data = (void *)(SCM_HAS_CORE_CLK
-                          | SCM_HAS_IFACE_CLK
-                          | SCM_HAS_BUS_CLK),
+       { .compatible = "qcom,scm-msm8974", .data = (void *)(SCM_HAS_CORE_CLK |
+                                                            SCM_HAS_IFACE_CLK |
+                                                            SCM_HAS_BUS_CLK)
        },
+       { .compatible = "qcom,scm-msm8996" },
+       { .compatible = "qcom,scm" },
        {}
 };
 
index ba79b609aca21068fc73b7b6f552137dfebdce87..684cb51694d1ff7a917b6883d5d00847698570d3 100644 (file)
@@ -33,7 +33,7 @@ config QCOM_GLINK_SSR
 
 config QCOM_GSBI
         tristate "QCOM General Serial Bus Interface"
-        depends on ARCH_QCOM
+        depends on ARCH_QCOM || COMPILE_TEST
         select MFD_SYSCON
         help
           Say y here to enable GSBI support.  The GSBI provides control
@@ -42,7 +42,7 @@ config QCOM_GSBI
 
 config QCOM_LLCC
        tristate "Qualcomm Technologies, Inc. LLCC driver"
-       depends on ARCH_QCOM
+       depends on ARCH_QCOM || COMPILE_TEST
        help
          Qualcomm Technologies, Inc. platform specific
          Last Level Cache Controller(LLCC) driver. This provides interfaces
@@ -73,7 +73,8 @@ config QCOM_PM
 
 config QCOM_QMI_HELPERS
        tristate
-       depends on ARCH_QCOM && NET
+       depends on ARCH_QCOM || COMPILE_TEST
+       depends on NET
        help
          Helper library for handling QMI encoded messages.  QMI encoded
          messages are used in communication between the majority of QRTR
@@ -94,7 +95,7 @@ config QCOM_RMTFS_MEM
 
 config QCOM_RPMH
        bool "Qualcomm RPM-Hardened (RPMH) Communication"
-       depends on ARCH_QCOM && ARM64 && OF || COMPILE_TEST
+       depends on ARCH_QCOM && ARM64 || COMPILE_TEST
        help
          Support for communication with the hardened-RPM blocks in
          Qualcomm Technologies Inc (QTI) SoCs. RPMH communication uses an
@@ -104,7 +105,7 @@ config QCOM_RPMH
 
 config QCOM_SMEM
        tristate "Qualcomm Shared Memory Manager (SMEM)"
-       depends on ARCH_QCOM
+       depends on ARCH_QCOM || COMPILE_TEST
        depends on HWSPINLOCK
        help
          Say y here to enable support for the Qualcomm Shared Memory Manager.
@@ -113,8 +114,8 @@ config QCOM_SMEM
 
 config QCOM_SMD_RPM
        tristate "Qualcomm Resource Power Manager (RPM) over SMD"
-       depends on ARCH_QCOM
-       depends on RPMSG && OF
+       depends on ARCH_QCOM || COMPILE_TEST
+       depends on RPMSG
        help
          If you say yes to this option, support will be included for the
          Resource Power Manager system found in the Qualcomm 8974 based
@@ -134,6 +135,7 @@ config QCOM_SMP2P
        depends on MAILBOX
        depends on QCOM_SMEM
        select QCOM_SMEM_STATE
+       select IRQ_DOMAIN
        help
          Say yes here to support the Qualcomm Shared Memory Point to Point
          protocol.
@@ -142,13 +144,14 @@ config QCOM_SMSM
        tristate "Qualcomm Shared Memory State Machine"
        depends on QCOM_SMEM
        select QCOM_SMEM_STATE
+       select IRQ_DOMAIN
        help
          Say yes here to support the Qualcomm Shared Memory State Machine.
          The state machine is represented by bits in shared memory.
 
 config QCOM_WCNSS_CTRL
        tristate "Qualcomm WCNSS control driver"
-       depends on ARCH_QCOM
+       depends on ARCH_QCOM || COMPILE_TEST
        depends on RPMSG
        help
          Client driver for the WCNSS_CTRL SMD channel, used to download nv
@@ -156,7 +159,7 @@ config QCOM_WCNSS_CTRL
 
 config QCOM_APR
        tristate "Qualcomm APR Bus (Asynchronous Packet Router)"
-       depends on ARCH_QCOM
+       depends on ARCH_QCOM || COMPILE_TEST
        depends on RPMSG
        help
           Enable APR IPC protocol support between
index 57af8a5373325f2895f08022629a723fb68a3d01..716762d59c1fb2da9f47ff5814be8d8348a26b8a 100644 (file)
@@ -87,7 +87,7 @@ static int apr_callback(struct rpmsg_device *rpdev, void *buf,
        }
 
        if (hdr->pkt_size < APR_HDR_SIZE || hdr->pkt_size != len) {
-               dev_err(apr->dev, "APR: Wrong paket size\n");
+               dev_err(apr->dev, "APR: Wrong packet size\n");
                return -EINVAL;
        }
 
@@ -219,9 +219,9 @@ static int apr_add_device(struct device *dev, struct device_node *np,
        adev->domain_id = id->domain_id;
        adev->version = id->svc_version;
        if (np)
-               strncpy(adev->name, np->name, APR_NAME_SIZE);
+               strscpy(adev->name, np->name, APR_NAME_SIZE);
        else
-               strncpy(adev->name, id->name, APR_NAME_SIZE);
+               strscpy(adev->name, id->name, APR_NAME_SIZE);
 
        dev_set_name(&adev->dev, "aprsvc:%s:%x:%x", adev->name,
                     id->domain_id, id->svc_id);
index 54063a31132fa0b21e2c1ee3ed5fd85c87e759c8..192ca761b2cb3a1f6c7615b3e179c94b0e1be51c 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/mutex.h>
 #include <linux/of_device.h>
 #include <linux/regmap.h>
+#include <linux/sizes.h>
 #include <linux/slab.h>
 #include <linux/soc/qcom/llcc-qcom.h>
 
@@ -106,22 +107,24 @@ static int llcc_update_act_ctrl(u32 sid,
        u32 slice_status;
        int ret;
 
-       act_ctrl_reg = drv_data->bcast_off + LLCC_TRP_ACT_CTRLn(sid);
-       status_reg = drv_data->bcast_off + LLCC_TRP_STATUSn(sid);
+       act_ctrl_reg = LLCC_TRP_ACT_CTRLn(sid);
+       status_reg = LLCC_TRP_STATUSn(sid);
 
        /* Set the ACTIVE trigger */
        act_ctrl_reg_val |= ACT_CTRL_ACT_TRIG;
-       ret = regmap_write(drv_data->regmap, act_ctrl_reg, act_ctrl_reg_val);
+       ret = regmap_write(drv_data->bcast_regmap, act_ctrl_reg,
+                               act_ctrl_reg_val);
        if (ret)
                return ret;
 
        /* Clear the ACTIVE trigger */
        act_ctrl_reg_val &= ~ACT_CTRL_ACT_TRIG;
-       ret = regmap_write(drv_data->regmap, act_ctrl_reg, act_ctrl_reg_val);
+       ret = regmap_write(drv_data->bcast_regmap, act_ctrl_reg,
+                               act_ctrl_reg_val);
        if (ret)
                return ret;
 
-       ret = regmap_read_poll_timeout(drv_data->regmap, status_reg,
+       ret = regmap_read_poll_timeout(drv_data->bcast_regmap, status_reg,
                                      slice_status, !(slice_status & status),
                                      0, LLCC_STATUS_READ_DELAY);
        return ret;
@@ -223,19 +226,16 @@ static int qcom_llcc_cfg_program(struct platform_device *pdev)
        u32 attr0_val;
        u32 max_cap_cacheline;
        u32 sz;
-       int ret;
+       int ret = 0;
        const struct llcc_slice_config *llcc_table;
        struct llcc_slice_desc desc;
-       u32 bcast_off = drv_data->bcast_off;
 
        sz = drv_data->cfg_size;
        llcc_table = drv_data->cfg;
 
        for (i = 0; i < sz; i++) {
-               attr1_cfg = bcast_off +
-                               LLCC_TRP_ATTR1_CFGn(llcc_table[i].slice_id);
-               attr0_cfg = bcast_off +
-                               LLCC_TRP_ATTR0_CFGn(llcc_table[i].slice_id);
+               attr1_cfg = LLCC_TRP_ATTR1_CFGn(llcc_table[i].slice_id);
+               attr0_cfg = LLCC_TRP_ATTR0_CFGn(llcc_table[i].slice_id);
 
                attr1_val = llcc_table[i].cache_mode;
                attr1_val |= llcc_table[i].probe_target_ways <<
@@ -260,10 +260,12 @@ static int qcom_llcc_cfg_program(struct platform_device *pdev)
                attr0_val = llcc_table[i].res_ways & ATTR0_RES_WAYS_MASK;
                attr0_val |= llcc_table[i].bonus_ways << ATTR0_BONUS_WAYS_SHIFT;
 
-               ret = regmap_write(drv_data->regmap, attr1_cfg, attr1_val);
+               ret = regmap_write(drv_data->bcast_regmap, attr1_cfg,
+                                       attr1_val);
                if (ret)
                        return ret;
-               ret = regmap_write(drv_data->regmap, attr0_cfg, attr0_val);
+               ret = regmap_write(drv_data->bcast_regmap, attr0_cfg,
+                                       attr0_val);
                if (ret)
                        return ret;
                if (llcc_table[i].activate_on_init) {
@@ -279,24 +281,37 @@ int qcom_llcc_probe(struct platform_device *pdev,
 {
        u32 num_banks;
        struct device *dev = &pdev->dev;
-       struct resource *res;
-       void __iomem *base;
+       struct resource *llcc_banks_res, *llcc_bcast_res;
+       void __iomem *llcc_banks_base, *llcc_bcast_base;
        int ret, i;
+       struct platform_device *llcc_edac;
 
        drv_data = devm_kzalloc(dev, sizeof(*drv_data), GFP_KERNEL);
        if (!drv_data)
                return -ENOMEM;
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       base = devm_ioremap_resource(&pdev->dev, res);
-       if (IS_ERR(base))
-               return PTR_ERR(base);
+       llcc_banks_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+                                                       "llcc_base");
+       llcc_banks_base = devm_ioremap_resource(&pdev->dev, llcc_banks_res);
+       if (IS_ERR(llcc_banks_base))
+               return PTR_ERR(llcc_banks_base);
 
-       drv_data->regmap = devm_regmap_init_mmio(dev, base,
-                                       &llcc_regmap_config);
+       drv_data->regmap = devm_regmap_init_mmio(dev, llcc_banks_base,
+                                               &llcc_regmap_config);
        if (IS_ERR(drv_data->regmap))
                return PTR_ERR(drv_data->regmap);
 
+       llcc_bcast_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+                                                       "llcc_broadcast_base");
+       llcc_bcast_base = devm_ioremap_resource(&pdev->dev, llcc_bcast_res);
+       if (IS_ERR(llcc_bcast_base))
+               return PTR_ERR(llcc_bcast_base);
+
+       drv_data->bcast_regmap = devm_regmap_init_mmio(dev, llcc_bcast_base,
+                                                       &llcc_regmap_config);
+       if (IS_ERR(drv_data->bcast_regmap))
+               return PTR_ERR(drv_data->bcast_regmap);
+
        ret = regmap_read(drv_data->regmap, LLCC_COMMON_STATUS0,
                                                &num_banks);
        if (ret)
@@ -318,8 +333,6 @@ int qcom_llcc_probe(struct platform_device *pdev,
        for (i = 0; i < num_banks; i++)
                drv_data->offsets[i] = i * BANK_OFFSET_STRIDE;
 
-       drv_data->bcast_off = num_banks * BANK_OFFSET_STRIDE;
-
        drv_data->bitmap = devm_kcalloc(dev,
        BITS_TO_LONGS(drv_data->max_slices), sizeof(unsigned long),
                                                GFP_KERNEL);
@@ -331,7 +344,20 @@ int qcom_llcc_probe(struct platform_device *pdev,
        mutex_init(&drv_data->lock);
        platform_set_drvdata(pdev, drv_data);
 
-       return qcom_llcc_cfg_program(pdev);
+       ret = qcom_llcc_cfg_program(pdev);
+       if (ret)
+               return ret;
+
+       drv_data->ecc_irq = platform_get_irq(pdev, 0);
+       if (drv_data->ecc_irq >= 0) {
+               llcc_edac = platform_device_register_data(&pdev->dev,
+                                               "qcom_llcc_edac", -1, drv_data,
+                                               sizeof(*drv_data));
+               if (IS_ERR(llcc_edac))
+                       dev_err(dev, "Failed to register llcc edac driver\n");
+       }
+
+       return ret;
 }
 EXPORT_SYMBOL_GPL(qcom_llcc_probe);
 
index feed3db21c10888681a0f205cccd24c73be078c1..ee89ffb6dde84cacc0f37a82918461b8b20eb206 100644 (file)
@@ -513,7 +513,7 @@ EXPORT_SYMBOL(geni_se_resources_on);
  */
 int geni_se_clk_tbl_get(struct geni_se *se, unsigned long **tbl)
 {
-       unsigned long freq = 0;
+       long freq = 0;
        int i;
 
        if (se->clk_perf_tbl) {
@@ -529,7 +529,7 @@ int geni_se_clk_tbl_get(struct geni_se *se, unsigned long **tbl)
 
        for (i = 0; i < MAX_CLK_PERF_LEVEL; i++) {
                freq = clk_round_rate(se->clk, freq + 1);
-               if (!freq || freq == se->clk_perf_tbl[i - 1])
+               if (freq <= 0 || freq == se->clk_perf_tbl[i - 1])
                        break;
                se->clk_perf_tbl[i] = freq;
        }
@@ -544,16 +544,17 @@ EXPORT_SYMBOL(geni_se_clk_tbl_get);
  * @se:                Pointer to the concerned serial engine.
  * @req_freq:  Requested clock frequency.
  * @index:     Index of the resultant frequency in the table.
- * @res_freq:  Resultant frequency which matches or is closer to the
- *             requested frequency.
+ * @res_freq:  Resultant frequency of the source clock.
  * @exact:     Flag to indicate exact multiple requirement of the requested
  *             frequency.
  *
- * This function is called by the protocol drivers to determine the matching
- * or exact multiple of the requested frequency, as provided by the serial
- * engine clock in order to meet the performance requirements. If there is
- * no matching or exact multiple of the requested frequency found, then it
- * selects the closest floor frequency, if exact flag is not set.
+ * This function is called by the protocol drivers to determine the best match
+ * of the requested frequency as provided by the serial engine clock in order
+ * to meet the performance requirements.
+ *
+ * If we return success:
+ * - if @exact is true  then @res_freq / <an_integer> == @req_freq
+ * - if @exact is false then @res_freq / <an_integer> <= @req_freq
  *
  * Return: 0 on success, standard Linux error codes on failure.
  */
@@ -564,6 +565,9 @@ int geni_se_clk_freq_match(struct geni_se *se, unsigned long req_freq,
        unsigned long *tbl;
        int num_clk_levels;
        int i;
+       unsigned long best_delta;
+       unsigned long new_delta;
+       unsigned int divider;
 
        num_clk_levels = geni_se_clk_tbl_get(se, &tbl);
        if (num_clk_levels < 0)
@@ -572,18 +576,21 @@ int geni_se_clk_freq_match(struct geni_se *se, unsigned long req_freq,
        if (num_clk_levels == 0)
                return -EINVAL;
 
-       *res_freq = 0;
+       best_delta = ULONG_MAX;
        for (i = 0; i < num_clk_levels; i++) {
-               if (!(tbl[i] % req_freq)) {
+               divider = DIV_ROUND_UP(tbl[i], req_freq);
+               new_delta = req_freq - tbl[i] / divider;
+               if (new_delta < best_delta) {
+                       /* We have a new best! */
                        *index = i;
                        *res_freq = tbl[i];
-                       return 0;
-               }
 
-               if (!(*res_freq) || ((tbl[i] > *res_freq) &&
-                                    (tbl[i] < req_freq))) {
-                       *index = i;
-                       *res_freq = tbl[i];
+                       /* If the new best is exact then we're done */
+                       if (new_delta == 0)
+                               return 0;
+
+                       /* Record how close we got */
+                       best_delta = new_delta;
                }
        }
 
index 8a3678c2e83cf609daddfdcd7c50e9de0a671ad2..97bb5989aa21158dee92d58e5f22bc74729dbbc2 100644 (file)
@@ -212,6 +212,11 @@ static int qcom_rmtfs_mem_probe(struct platform_device *pdev)
                dev_err(&pdev->dev, "failed to parse qcom,vmid\n");
                goto remove_cdev;
        } else if (!ret) {
+               if (!qcom_scm_is_available()) {
+                       ret = -EPROBE_DEFER;
+                       goto remove_cdev;
+               }
+
                perms[0].vmid = QCOM_SCM_VMID_HLOS;
                perms[0].perm = QCOM_SCM_PERM_RW;
                perms[1].vmid = vmid;
index ee75da66d64bf06466791a41182bae0e64e1643f..75bd9a83aef00670d474a69c86fde81aa4794e93 100644 (file)
@@ -121,6 +121,7 @@ static int tcs_invalidate(struct rsc_drv *drv, int type)
                        return -EAGAIN;
                }
                write_tcs_reg_sync(drv, RSC_DRV_CMD_ENABLE, m, 0);
+               write_tcs_reg_sync(drv, RSC_DRV_CMD_WAIT_FOR_CMPL, m, 0);
        }
        bitmap_zero(tcs->slots, MAX_TCS_SLOTS);
        spin_unlock(&tcs->lock);
@@ -239,6 +240,7 @@ static irqreturn_t tcs_tx_done(int irq, void *p)
 skip:
                /* Reclaim the TCS */
                write_tcs_reg(drv, RSC_DRV_CMD_ENABLE, i, 0);
+               write_tcs_reg(drv, RSC_DRV_CMD_WAIT_FOR_CMPL, i, 0);
                write_tcs_reg(drv, RSC_DRV_IRQ_CLEAR, 0, BIT(i));
                spin_lock(&drv->lock);
                clear_bit(i, drv->tcs_in_use);
index bf4bd71ab53fd9a36460b7d1a1f52793ea998506..f80d040601fd7cfa75efc6180ba59fc19b6ecb93 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/of.h>
 #include <linux/of_address.h>
 #include <linux/platform_device.h>
+#include <linux/sizes.h>
 #include <linux/slab.h>
 #include <linux/soc/qcom/smem.h>
 
@@ -277,7 +278,7 @@ struct qcom_smem {
        u32 item_count;
 
        unsigned num_regions;
-       struct smem_region regions[0];
+       struct smem_region regions[];
 };
 
 static void *
@@ -489,7 +490,7 @@ static void *qcom_smem_get_global(struct qcom_smem *smem,
                                  size_t *size)
 {
        struct smem_header *header;
-       struct smem_region *area;
+       struct smem_region *region;
        struct smem_global_entry *entry;
        u32 aux_base;
        unsigned i;
@@ -502,12 +503,12 @@ static void *qcom_smem_get_global(struct qcom_smem *smem,
        aux_base = le32_to_cpu(entry->aux_base) & AUX_BASE_MASK;
 
        for (i = 0; i < smem->num_regions; i++) {
-               area = &smem->regions[i];
+               region = &smem->regions[i];
 
-               if (area->aux_base == aux_base || !aux_base) {
+               if (region->aux_base == aux_base || !aux_base) {
                        if (size != NULL)
                                *size = le32_to_cpu(entry->size);
-                       return area->virt_base + le32_to_cpu(entry->offset);
+                       return region->virt_base + le32_to_cpu(entry->offset);
                }
        }
 
@@ -722,12 +723,59 @@ static u32 qcom_smem_get_item_count(struct qcom_smem *smem)
        return le16_to_cpu(info->num_items);
 }
 
+/*
+ * Validate the partition header for a partition whose partition
+ * table entry is supplied.  Returns a pointer to its header if
+ * valid, or a null pointer otherwise.
+ */
+static struct smem_partition_header *
+qcom_smem_partition_header(struct qcom_smem *smem,
+               struct smem_ptable_entry *entry, u16 host0, u16 host1)
+{
+       struct smem_partition_header *header;
+       u32 size;
+
+       header = smem->regions[0].virt_base + le32_to_cpu(entry->offset);
+
+       if (memcmp(header->magic, SMEM_PART_MAGIC, sizeof(header->magic))) {
+               dev_err(smem->dev, "bad partition magic %02x %02x %02x %02x\n",
+                       header->magic[0], header->magic[1],
+                       header->magic[2], header->magic[3]);
+               return NULL;
+       }
+
+       if (host0 != le16_to_cpu(header->host0)) {
+               dev_err(smem->dev, "bad host0 (%hu != %hu)\n",
+                               host0, le16_to_cpu(header->host0));
+               return NULL;
+       }
+       if (host1 != le16_to_cpu(header->host1)) {
+               dev_err(smem->dev, "bad host1 (%hu != %hu)\n",
+                               host1, le16_to_cpu(header->host1));
+               return NULL;
+       }
+
+       size = le32_to_cpu(header->size);
+       if (size != le32_to_cpu(entry->size)) {
+               dev_err(smem->dev, "bad partition size (%u != %u)\n",
+                       size, le32_to_cpu(entry->size));
+               return NULL;
+       }
+
+       if (le32_to_cpu(header->offset_free_uncached) > size) {
+               dev_err(smem->dev, "bad partition free uncached (%u > %u)\n",
+                       le32_to_cpu(header->offset_free_uncached), size);
+               return NULL;
+       }
+
+       return header;
+}
+
 static int qcom_smem_set_global_partition(struct qcom_smem *smem)
 {
        struct smem_partition_header *header;
        struct smem_ptable_entry *entry;
        struct smem_ptable *ptable;
-       u32 host0, host1, size;
        bool found = false;
        int i;
 
@@ -742,10 +790,15 @@ static int qcom_smem_set_global_partition(struct qcom_smem *smem)
 
        for (i = 0; i < le32_to_cpu(ptable->num_entries); i++) {
                entry = &ptable->entry[i];
-               host0 = le16_to_cpu(entry->host0);
-               host1 = le16_to_cpu(entry->host1);
+               if (!le32_to_cpu(entry->offset))
+                       continue;
+               if (!le32_to_cpu(entry->size))
+                       continue;
+
+               if (le16_to_cpu(entry->host0) != SMEM_GLOBAL_HOST)
+                       continue;
 
-               if (host0 == SMEM_GLOBAL_HOST && host0 == host1) {
+               if (le16_to_cpu(entry->host1) == SMEM_GLOBAL_HOST) {
                        found = true;
                        break;
                }
@@ -756,36 +809,10 @@ static int qcom_smem_set_global_partition(struct qcom_smem *smem)
                return -EINVAL;
        }
 
-       if (!le32_to_cpu(entry->offset) || !le32_to_cpu(entry->size)) {
-               dev_err(smem->dev, "Invalid entry for global partition\n");
+       header = qcom_smem_partition_header(smem, entry,
+                               SMEM_GLOBAL_HOST, SMEM_GLOBAL_HOST);
+       if (!header)
                return -EINVAL;
-       }
-
-       header = smem->regions[0].virt_base + le32_to_cpu(entry->offset);
-       host0 = le16_to_cpu(header->host0);
-       host1 = le16_to_cpu(header->host1);
-
-       if (memcmp(header->magic, SMEM_PART_MAGIC, sizeof(header->magic))) {
-               dev_err(smem->dev, "Global partition has invalid magic\n");
-               return -EINVAL;
-       }
-
-       if (host0 != SMEM_GLOBAL_HOST && host1 != SMEM_GLOBAL_HOST) {
-               dev_err(smem->dev, "Global partition hosts are invalid\n");
-               return -EINVAL;
-       }
-
-       if (le32_to_cpu(header->size) != le32_to_cpu(entry->size)) {
-               dev_err(smem->dev, "Global partition has invalid size\n");
-               return -EINVAL;
-       }
-
-       size = le32_to_cpu(header->offset_free_uncached);
-       if (size > le32_to_cpu(header->size)) {
-               dev_err(smem->dev,
-                       "Global partition has invalid free pointer\n");
-               return -EINVAL;
-       }
 
        smem->global_partition = header;
        smem->global_cacheline = le32_to_cpu(entry->cacheline);
@@ -793,14 +820,14 @@ static int qcom_smem_set_global_partition(struct qcom_smem *smem)
        return 0;
 }
 
-static int qcom_smem_enumerate_partitions(struct qcom_smem *smem,
-                                         unsigned int local_host)
+static int
+qcom_smem_enumerate_partitions(struct qcom_smem *smem, u16 local_host)
 {
        struct smem_partition_header *header;
        struct smem_ptable_entry *entry;
        struct smem_ptable *ptable;
        unsigned int remote_host;
-       u32 host0, host1;
+       u16 host0, host1;
        int i;
 
        ptable = qcom_smem_get_ptable(smem);
@@ -809,71 +836,33 @@ static int qcom_smem_enumerate_partitions(struct qcom_smem *smem,
 
        for (i = 0; i < le32_to_cpu(ptable->num_entries); i++) {
                entry = &ptable->entry[i];
-               host0 = le16_to_cpu(entry->host0);
-               host1 = le16_to_cpu(entry->host1);
-
-               if (host0 != local_host && host1 != local_host)
-                       continue;
-
                if (!le32_to_cpu(entry->offset))
                        continue;
-
                if (!le32_to_cpu(entry->size))
                        continue;
 
+               host0 = le16_to_cpu(entry->host0);
+               host1 = le16_to_cpu(entry->host1);
                if (host0 == local_host)
                        remote_host = host1;
-               else
+               else if (host1 == local_host)
                        remote_host = host0;
+               else
+                       continue;
 
                if (remote_host >= SMEM_HOST_COUNT) {
-                       dev_err(smem->dev,
-                               "Invalid remote host %d\n",
-                               remote_host);
+                       dev_err(smem->dev, "bad host %hu\n", remote_host);
                        return -EINVAL;
                }
 
                if (smem->partitions[remote_host]) {
-                       dev_err(smem->dev,
-                               "Already found a partition for host %d\n",
-                               remote_host);
-                       return -EINVAL;
-               }
-
-               header = smem->regions[0].virt_base + le32_to_cpu(entry->offset);
-               host0 = le16_to_cpu(header->host0);
-               host1 = le16_to_cpu(header->host1);
-
-               if (memcmp(header->magic, SMEM_PART_MAGIC,
-                           sizeof(header->magic))) {
-                       dev_err(smem->dev,
-                               "Partition %d has invalid magic\n", i);
+                       dev_err(smem->dev, "duplicate host %hu\n", remote_host);
                        return -EINVAL;
                }
 
-               if (host0 != local_host && host1 != local_host) {
-                       dev_err(smem->dev,
-                               "Partition %d hosts are invalid\n", i);
+               header = qcom_smem_partition_header(smem, entry, host0, host1);
+               if (!header)
                        return -EINVAL;
-               }
-
-               if (host0 != remote_host && host1 != remote_host) {
-                       dev_err(smem->dev,
-                               "Partition %d hosts are invalid\n", i);
-                       return -EINVAL;
-               }
-
-               if (le32_to_cpu(header->size) != le32_to_cpu(entry->size)) {
-                       dev_err(smem->dev,
-                               "Partition %d has invalid size\n", i);
-                       return -EINVAL;
-               }
-
-               if (le32_to_cpu(header->offset_free_uncached) > le32_to_cpu(header->size)) {
-                       dev_err(smem->dev,
-                               "Partition %d has invalid free pointer\n", i);
-                       return -EINVAL;
-               }
 
                smem->partitions[remote_host] = header;
                smem->cacheline[remote_host] = le32_to_cpu(entry->cacheline);
@@ -887,6 +876,7 @@ static int qcom_smem_map_memory(struct qcom_smem *smem, struct device *dev,
 {
        struct device_node *np;
        struct resource r;
+       resource_size_t size;
        int ret;
 
        np = of_parse_phandle(dev->of_node, name, 0);
@@ -899,12 +889,13 @@ static int qcom_smem_map_memory(struct qcom_smem *smem, struct device *dev,
        of_node_put(np);
        if (ret)
                return ret;
+       size = resource_size(&r);
 
-       smem->regions[i].aux_base = (u32)r.start;
-       smem->regions[i].size = resource_size(&r);
-       smem->regions[i].virt_base = devm_ioremap_wc(dev, r.start, resource_size(&r));
+       smem->regions[i].virt_base = devm_ioremap_wc(dev, r.start, size);
        if (!smem->regions[i].virt_base)
                return -ENOMEM;
+       smem->regions[i].aux_base = (u32)r.start;
+       smem->regions[i].size = size;
 
        return 0;
 }
@@ -962,6 +953,7 @@ static int qcom_smem_probe(struct platform_device *pdev)
                return -EINVAL;
        }
 
+       BUILD_BUG_ON(SMEM_HOST_APPS >= SMEM_HOST_COUNT);
        ret = qcom_smem_enumerate_partitions(smem, SMEM_HOST_APPS);
        if (ret < 0 && ret != -ENOENT)
                return ret;
index f9d7a85b2822036dd1cdd7497032c98f2f7ea383..53807e839664e9f3ca70e92d9e5a5e336e38b4c0 100644 (file)
@@ -219,6 +219,9 @@ static int __init qcom_cpuidle_init(struct device_node *cpu_node, int cpu)
        cpumask_t mask;
        bool use_scm_power_down = false;
 
+       if (!qcom_scm_is_available())
+               return -EPROBE_DEFER;
+
        for (i = 0; ; i++) {
                state_node = of_parse_phandle(cpu_node, "cpu-idle-states", i);
                if (!state_node)
index df3ccb30bc2dddba0d2d6accccdd6a6c5a7bc53c..373400dd816d6802378dcf5bc7373fb921b22343 100644 (file)
@@ -281,7 +281,7 @@ struct rpmsg_endpoint *qcom_wcnss_open_channel(void *wcnss, const char *name, rp
        struct rpmsg_channel_info chinfo;
        struct wcnss_ctrl *_wcnss = wcnss;
 
-       strncpy(chinfo.name, name, sizeof(chinfo.name));
+       strscpy(chinfo.name, name, sizeof(chinfo.name));
        chinfo.src = RPMSG_ADDR_ANY;
        chinfo.dst = RPMSG_ADDR_ANY;
 
index 5d61449778287c508d36377792984a7b15e5ccce..3bcd67fd554851bbf7f928989d5f78817d547efc 100644 (file)
@@ -225,19 +225,14 @@ struct geni_se {
 #define HW_VER_MINOR_SHFT              16
 #define HW_VER_STEP_MASK               GENMASK(15, 0)
 
+#define GENI_SE_VERSION_MAJOR(ver) ((ver & HW_VER_MAJOR_MASK) >> HW_VER_MAJOR_SHFT)
+#define GENI_SE_VERSION_MINOR(ver) ((ver & HW_VER_MINOR_MASK) >> HW_VER_MINOR_SHFT)
+#define GENI_SE_VERSION_STEP(ver) (ver & HW_VER_STEP_MASK)
+
 #if IS_ENABLED(CONFIG_QCOM_GENI_SE)
 
 u32 geni_se_get_qup_hw_version(struct geni_se *se);
 
-#define geni_se_get_wrapper_version(se, major, minor, step) do { \
-       u32 ver; \
-\
-       ver = geni_se_get_qup_hw_version(se); \
-       major = (ver & HW_VER_MAJOR_MASK) >> HW_VER_MAJOR_SHFT; \
-       minor = (ver & HW_VER_MINOR_MASK) >> HW_VER_MINOR_SHFT; \
-       step = version & HW_VER_STEP_MASK; \
-} while (0)
-
 /**
  * geni_se_read_proto() - Read the protocol configured for a serial engine
  * @se:                Pointer to the concerned serial engine.
index 7e3b9c605ab2cd5b8c9a2a40d686826de96a3c56..69c285b1c9905496ff8b4f1d05c6b5221278075e 100644 (file)
@@ -70,25 +70,51 @@ struct llcc_slice_config {
 /**
  * llcc_drv_data - Data associated with the llcc driver
  * @regmap: regmap associated with the llcc device
+ * @bcast_regmap: regmap associated with llcc broadcast offset
  * @cfg: pointer to the data structure for slice configuration
  * @lock: mutex associated with each slice
  * @cfg_size: size of the config data table
  * @max_slices: max slices as read from device tree
- * @bcast_off: Offset of the broadcast bank
  * @num_banks: Number of llcc banks
  * @bitmap: Bit map to track the active slice ids
  * @offsets: Pointer to the bank offsets array
+ * @ecc_irq: interrupt for llcc cache error detection and reporting
  */
 struct llcc_drv_data {
        struct regmap *regmap;
+       struct regmap *bcast_regmap;
        const struct llcc_slice_config *cfg;
        struct mutex lock;
        u32 cfg_size;
        u32 max_slices;
-       u32 bcast_off;
        u32 num_banks;
        unsigned long *bitmap;
        u32 *offsets;
+       int ecc_irq;
+};
+
+/**
+ * llcc_edac_reg_data - llcc edac registers data for each error type
+ * @name: Name of the error
+ * @synd_reg: Syndrome register address
+ * @count_status_reg: Status register address to read the error count
+ * @ways_status_reg: Status register address to read the error ways
+ * @reg_cnt: Number of registers
+ * @count_mask: Mask value to get the error count
+ * @ways_mask: Mask value to get the error ways
+ * @count_shift: Shift value to get the error count
+ * @ways_shift: Shift value to get the error ways
+ */
+struct llcc_edac_reg_data {
+       char *name;
+       u64 synd_reg;
+       u64 count_status_reg;
+       u64 ways_status_reg;
+       u32 reg_cnt;
+       u32 count_mask;
+       u32 ways_mask;
+       u8  count_shift;
+       u8  ways_shift;
 };
 
 #if IS_ENABLED(CONFIG_QCOM_LLCC)