mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-18 02:04:05 +08:00
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 -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJbsQupAAoJEFKiBbHx2RXVQBYQAKWr3RsQlWJ09M0vm8qGeCCA 8FXxX9wqcU34KvYR16IIXzQ/25L4h1uLjvjBfQurn7zOFIrCoqn2PGOchGOyJEA0 hPw+RomT17BV1hRSJkl8lIXMqGCAvoc44VrAQyipCTpEJO82emh6qJ1ki5XpIvfI EOad6S2IGNXU39F0B6rtl4+LL/Vfh+MOJcz0E23SVujtDT0pvxuP5aVGM+Faqif/ rQ4dKcYZokRHmuTlfg2azo4OmkLyrwsMjirna3NN3MX14wK5Ox53ESplGZXndt3V Gy48IbXdTTjbmHT07aqPVSkFgulkaE9Z3MTw+vyn/r10ww6R55XtoSJIbS/v4+Y0 mzjTYTSHmxzpGHR4Rs+E1q9/7y8tbxO0uzFMHlmD8fNNbkK3FlXymGfVJFjSJGSN hTN97zEYUKco4M6RXAly8XUrOetSglZwul1+gQnHYYCx7Y8BhgpyZtyjVeKzg1Mw wqgAi+1+cOLG2JjI+h0Xo0xqvuWwl3e2jrmBZNs9BWoXaaWA2L7oqPL3bxq2TeWs kF56I7YH5eD5sUGvShjrJ9WfKeN4hWpYSif/qg58kqz/zbCSjmmfiqQ7HmjJUetm JnAZaik38YhsDvR8K1gECQiN2myjojjdyg3ONBk2y0Yho+ClVSMPo/qCceRy21/q LVTRThQ0tZExH8zEJrJt =rx3Z -----END PGP SIGNATURE----- Merge tag 'qcom-drivers-for-4.20' of git://git.kernel.org/pub/scm/linux/kernel/git/agross/linux into next/drivers 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>
This commit is contained in:
commit
64d20b774f
@ -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>;
|
||||
};
|
||||
|
@ -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";
|
||||
};
|
||||
};
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
414
drivers/edac/qcom_edac.c
Normal file
414
drivers/edac/qcom_edac.c
Normal file
@ -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");
|
@ -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-apq8084", .data = (void *)(SCM_HAS_CORE_CLK |
|
||||
SCM_HAS_IFACE_CLK |
|
||||
SCM_HAS_BUS_CLK)
|
||||
},
|
||||
{ .compatible = "qcom,scm-msm8960",
|
||||
.data = (void *) SCM_HAS_CORE_CLK,
|
||||
{ .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-msm8996",
|
||||
.data = NULL, /* no clocks */
|
||||
},
|
||||
{ .compatible = "qcom,scm-ipq4019",
|
||||
.data = NULL, /* no clocks */
|
||||
},
|
||||
{ .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" },
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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 (host0 == SMEM_GLOBAL_HOST && host0 == host1) {
|
||||
if (le16_to_cpu(entry->host0) != SMEM_GLOBAL_HOST)
|
||||
continue;
|
||||
|
||||
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);
|
||||
dev_err(smem->dev, "duplicate host %hu\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);
|
||||
header = qcom_smem_partition_header(smem, entry, host0, host1);
|
||||
if (!header)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (host0 != local_host && host1 != local_host) {
|
||||
dev_err(smem->dev,
|
||||
"Partition %d hosts are invalid\n", i);
|
||||
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;
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user