mirror of https://gitee.com/openkylin/linux.git
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:
|
- reg:
|
||||||
Usage: required
|
Usage: required
|
||||||
Value Type: <prop-encoded-array>
|
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:
|
Example:
|
||||||
|
|
||||||
cache-controller@1100000 {
|
cache-controller@1100000 {
|
||||||
compatible = "qcom,sdm845-llcc";
|
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:
|
Required properties:
|
||||||
- compatible: must contain one of the following:
|
- compatible: must contain one of the following:
|
||||||
* "qcom,scm-apq8064" for APQ8064 platforms
|
* "qcom,scm-apq8064"
|
||||||
* "qcom,scm-msm8660" for MSM8660 platforms
|
* "qcom,scm-apq8084"
|
||||||
* "qcom,scm-msm8690" for MSM8690 platforms
|
* "qcom,scm-msm8660"
|
||||||
* "qcom,scm-msm8996" for MSM8996 platforms
|
* "qcom,scm-msm8916"
|
||||||
* "qcom,scm-ipq4019" for IPQ4019 platforms
|
* "qcom,scm-msm8960"
|
||||||
* "qcom,scm" for later processors (MSM8916, APQ8084, MSM8974, etc)
|
* "qcom,scm-msm8974"
|
||||||
- clocks: One to three clocks may be required based on compatible.
|
* "qcom,scm-msm8996"
|
||||||
* No clock required for "qcom,scm-msm8996", "qcom,scm-ipq4019"
|
* "qcom,scm-msm8998"
|
||||||
* Only core clock required for "qcom,scm-apq8064", "qcom,scm-msm8660", and "qcom,scm-msm8960"
|
* "qcom,scm-ipq4019"
|
||||||
* Core, iface, and bus clocks required for "qcom,scm"
|
* "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-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.
|
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
|
- qcom,dload-mode: phandle to the TCSR hardware block and offset of the
|
||||||
|
@ -26,8 +33,10 @@ Example for MSM8916:
|
||||||
|
|
||||||
firmware {
|
firmware {
|
||||||
scm {
|
scm {
|
||||||
compatible = "qcom,scm";
|
compatible = "qcom,msm8916", "qcom,scm";
|
||||||
clocks = <&gcc GCC_CRYPTO_CLK> , <&gcc GCC_CRYPTO_AXI_CLK>, <&gcc GCC_CRYPTO_AHB_CLK>;
|
clocks = <&gcc GCC_CRYPTO_CLK> ,
|
||||||
|
<&gcc GCC_CRYPTO_AXI_CLK>,
|
||||||
|
<&gcc GCC_CRYPTO_AHB_CLK>;
|
||||||
clock-names = "core", "bus", "iface";
|
clock-names = "core", "bus", "iface";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -5347,6 +5347,14 @@ L: linux-edac@vger.kernel.org
|
||||||
S: Maintained
|
S: Maintained
|
||||||
F: drivers/edac/ti_edac.c
|
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
|
EDIROL UA-101/UA-1000 DRIVER
|
||||||
M: Clemens Ladisch <clemens@ladisch.de>
|
M: Clemens Ladisch <clemens@ladisch.de>
|
||||||
L: alsa-devel@alsa-project.org (moderated for non-subscribers)
|
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
|
Support for error detection and correction on the
|
||||||
TI SoCs.
|
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
|
endif # EDAC
|
||||||
|
|
|
@ -77,3 +77,4 @@ obj-$(CONFIG_EDAC_ALTERA) += altera_edac.o
|
||||||
obj-$(CONFIG_EDAC_SYNOPSYS) += synopsys_edac.o
|
obj-$(CONFIG_EDAC_SYNOPSYS) += synopsys_edac.o
|
||||||
obj-$(CONFIG_EDAC_XGENE) += xgene_edac.o
|
obj-$(CONFIG_EDAC_XGENE) += xgene_edac.o
|
||||||
obj-$(CONFIG_EDAC_TI) += ti_edac.o
|
obj-$(CONFIG_EDAC_TI) += ti_edac.o
|
||||||
|
obj-$(CONFIG_EDAC_QCOM) += qcom_edac.o
|
||||||
|
|
|
@ -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;
|
return ret;
|
||||||
|
|
||||||
clks = (unsigned long)of_device_get_match_data(&pdev->dev);
|
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");
|
scm->core_clk = devm_clk_get(&pdev->dev, "core");
|
||||||
if (IS_ERR(scm->core_clk)) {
|
if (IS_ERR(scm->core_clk)) {
|
||||||
if (PTR_ERR(scm->core_clk) != -EPROBE_DEFER)
|
if (PTR_ERR(scm->core_clk) == -EPROBE_DEFER)
|
||||||
dev_err(&pdev->dev,
|
return PTR_ERR(scm->core_clk);
|
||||||
"failed to acquire core clk\n");
|
|
||||||
|
if (clks & SCM_HAS_CORE_CLK) {
|
||||||
|
dev_err(&pdev->dev, "failed to acquire core clk\n");
|
||||||
return PTR_ERR(scm->core_clk);
|
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");
|
scm->iface_clk = devm_clk_get(&pdev->dev, "iface");
|
||||||
if (IS_ERR(scm->iface_clk)) {
|
if (IS_ERR(scm->iface_clk)) {
|
||||||
if (PTR_ERR(scm->iface_clk) != -EPROBE_DEFER)
|
if (PTR_ERR(scm->iface_clk) == -EPROBE_DEFER)
|
||||||
dev_err(&pdev->dev,
|
return PTR_ERR(scm->iface_clk);
|
||||||
"failed to acquire iface clk\n");
|
|
||||||
|
if (clks & SCM_HAS_IFACE_CLK) {
|
||||||
|
dev_err(&pdev->dev, "failed to acquire iface clk\n");
|
||||||
return PTR_ERR(scm->iface_clk);
|
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");
|
scm->bus_clk = devm_clk_get(&pdev->dev, "bus");
|
||||||
if (IS_ERR(scm->bus_clk)) {
|
if (IS_ERR(scm->bus_clk)) {
|
||||||
if (PTR_ERR(scm->bus_clk) != -EPROBE_DEFER)
|
if (PTR_ERR(scm->bus_clk) == -EPROBE_DEFER)
|
||||||
dev_err(&pdev->dev,
|
return PTR_ERR(scm->bus_clk);
|
||||||
"failed to acquire bus clk\n");
|
|
||||||
|
if (clks & SCM_HAS_BUS_CLK) {
|
||||||
|
dev_err(&pdev->dev, "failed to acquire bus clk\n");
|
||||||
return PTR_ERR(scm->bus_clk);
|
return PTR_ERR(scm->bus_clk);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
scm->bus_clk = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
scm->reset.ops = &qcom_scm_pas_reset_ops;
|
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",
|
{ .compatible = "qcom,scm-apq8064",
|
||||||
/* FIXME: This should have .data = (void *) SCM_HAS_CORE_CLK */
|
/* FIXME: This should have .data = (void *) SCM_HAS_CORE_CLK */
|
||||||
},
|
},
|
||||||
{ .compatible = "qcom,scm-msm8660",
|
{ .compatible = "qcom,scm-apq8084", .data = (void *)(SCM_HAS_CORE_CLK |
|
||||||
.data = (void *) SCM_HAS_CORE_CLK,
|
SCM_HAS_IFACE_CLK |
|
||||||
|
SCM_HAS_BUS_CLK)
|
||||||
},
|
},
|
||||||
{ .compatible = "qcom,scm-msm8960",
|
{ .compatible = "qcom,scm-ipq4019" },
|
||||||
.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-msm8916", .data = (void *)(SCM_HAS_CORE_CLK |
|
||||||
|
SCM_HAS_IFACE_CLK |
|
||||||
|
SCM_HAS_BUS_CLK)
|
||||||
},
|
},
|
||||||
{ .compatible = "qcom,scm-msm8996",
|
{ .compatible = "qcom,scm-msm8974", .data = (void *)(SCM_HAS_CORE_CLK |
|
||||||
.data = NULL, /* no clocks */
|
SCM_HAS_IFACE_CLK |
|
||||||
},
|
SCM_HAS_BUS_CLK)
|
||||||
{ .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-msm8996" },
|
||||||
|
{ .compatible = "qcom,scm" },
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ config QCOM_GLINK_SSR
|
||||||
|
|
||||||
config QCOM_GSBI
|
config QCOM_GSBI
|
||||||
tristate "QCOM General Serial Bus Interface"
|
tristate "QCOM General Serial Bus Interface"
|
||||||
depends on ARCH_QCOM
|
depends on ARCH_QCOM || COMPILE_TEST
|
||||||
select MFD_SYSCON
|
select MFD_SYSCON
|
||||||
help
|
help
|
||||||
Say y here to enable GSBI support. The GSBI provides control
|
Say y here to enable GSBI support. The GSBI provides control
|
||||||
|
@ -42,7 +42,7 @@ config QCOM_GSBI
|
||||||
|
|
||||||
config QCOM_LLCC
|
config QCOM_LLCC
|
||||||
tristate "Qualcomm Technologies, Inc. LLCC driver"
|
tristate "Qualcomm Technologies, Inc. LLCC driver"
|
||||||
depends on ARCH_QCOM
|
depends on ARCH_QCOM || COMPILE_TEST
|
||||||
help
|
help
|
||||||
Qualcomm Technologies, Inc. platform specific
|
Qualcomm Technologies, Inc. platform specific
|
||||||
Last Level Cache Controller(LLCC) driver. This provides interfaces
|
Last Level Cache Controller(LLCC) driver. This provides interfaces
|
||||||
|
@ -73,7 +73,8 @@ config QCOM_PM
|
||||||
|
|
||||||
config QCOM_QMI_HELPERS
|
config QCOM_QMI_HELPERS
|
||||||
tristate
|
tristate
|
||||||
depends on ARCH_QCOM && NET
|
depends on ARCH_QCOM || COMPILE_TEST
|
||||||
|
depends on NET
|
||||||
help
|
help
|
||||||
Helper library for handling QMI encoded messages. QMI encoded
|
Helper library for handling QMI encoded messages. QMI encoded
|
||||||
messages are used in communication between the majority of QRTR
|
messages are used in communication between the majority of QRTR
|
||||||
|
@ -94,7 +95,7 @@ config QCOM_RMTFS_MEM
|
||||||
|
|
||||||
config QCOM_RPMH
|
config QCOM_RPMH
|
||||||
bool "Qualcomm RPM-Hardened (RPMH) Communication"
|
bool "Qualcomm RPM-Hardened (RPMH) Communication"
|
||||||
depends on ARCH_QCOM && ARM64 && OF || COMPILE_TEST
|
depends on ARCH_QCOM && ARM64 || COMPILE_TEST
|
||||||
help
|
help
|
||||||
Support for communication with the hardened-RPM blocks in
|
Support for communication with the hardened-RPM blocks in
|
||||||
Qualcomm Technologies Inc (QTI) SoCs. RPMH communication uses an
|
Qualcomm Technologies Inc (QTI) SoCs. RPMH communication uses an
|
||||||
|
@ -104,7 +105,7 @@ config QCOM_RPMH
|
||||||
|
|
||||||
config QCOM_SMEM
|
config QCOM_SMEM
|
||||||
tristate "Qualcomm Shared Memory Manager (SMEM)"
|
tristate "Qualcomm Shared Memory Manager (SMEM)"
|
||||||
depends on ARCH_QCOM
|
depends on ARCH_QCOM || COMPILE_TEST
|
||||||
depends on HWSPINLOCK
|
depends on HWSPINLOCK
|
||||||
help
|
help
|
||||||
Say y here to enable support for the Qualcomm Shared Memory Manager.
|
Say y here to enable support for the Qualcomm Shared Memory Manager.
|
||||||
|
@ -113,8 +114,8 @@ config QCOM_SMEM
|
||||||
|
|
||||||
config QCOM_SMD_RPM
|
config QCOM_SMD_RPM
|
||||||
tristate "Qualcomm Resource Power Manager (RPM) over SMD"
|
tristate "Qualcomm Resource Power Manager (RPM) over SMD"
|
||||||
depends on ARCH_QCOM
|
depends on ARCH_QCOM || COMPILE_TEST
|
||||||
depends on RPMSG && OF
|
depends on RPMSG
|
||||||
help
|
help
|
||||||
If you say yes to this option, support will be included for the
|
If you say yes to this option, support will be included for the
|
||||||
Resource Power Manager system found in the Qualcomm 8974 based
|
Resource Power Manager system found in the Qualcomm 8974 based
|
||||||
|
@ -134,6 +135,7 @@ config QCOM_SMP2P
|
||||||
depends on MAILBOX
|
depends on MAILBOX
|
||||||
depends on QCOM_SMEM
|
depends on QCOM_SMEM
|
||||||
select QCOM_SMEM_STATE
|
select QCOM_SMEM_STATE
|
||||||
|
select IRQ_DOMAIN
|
||||||
help
|
help
|
||||||
Say yes here to support the Qualcomm Shared Memory Point to Point
|
Say yes here to support the Qualcomm Shared Memory Point to Point
|
||||||
protocol.
|
protocol.
|
||||||
|
@ -142,13 +144,14 @@ config QCOM_SMSM
|
||||||
tristate "Qualcomm Shared Memory State Machine"
|
tristate "Qualcomm Shared Memory State Machine"
|
||||||
depends on QCOM_SMEM
|
depends on QCOM_SMEM
|
||||||
select QCOM_SMEM_STATE
|
select QCOM_SMEM_STATE
|
||||||
|
select IRQ_DOMAIN
|
||||||
help
|
help
|
||||||
Say yes here to support the Qualcomm Shared Memory State Machine.
|
Say yes here to support the Qualcomm Shared Memory State Machine.
|
||||||
The state machine is represented by bits in shared memory.
|
The state machine is represented by bits in shared memory.
|
||||||
|
|
||||||
config QCOM_WCNSS_CTRL
|
config QCOM_WCNSS_CTRL
|
||||||
tristate "Qualcomm WCNSS control driver"
|
tristate "Qualcomm WCNSS control driver"
|
||||||
depends on ARCH_QCOM
|
depends on ARCH_QCOM || COMPILE_TEST
|
||||||
depends on RPMSG
|
depends on RPMSG
|
||||||
help
|
help
|
||||||
Client driver for the WCNSS_CTRL SMD channel, used to download nv
|
Client driver for the WCNSS_CTRL SMD channel, used to download nv
|
||||||
|
@ -156,7 +159,7 @@ config QCOM_WCNSS_CTRL
|
||||||
|
|
||||||
config QCOM_APR
|
config QCOM_APR
|
||||||
tristate "Qualcomm APR Bus (Asynchronous Packet Router)"
|
tristate "Qualcomm APR Bus (Asynchronous Packet Router)"
|
||||||
depends on ARCH_QCOM
|
depends on ARCH_QCOM || COMPILE_TEST
|
||||||
depends on RPMSG
|
depends on RPMSG
|
||||||
help
|
help
|
||||||
Enable APR IPC protocol support between
|
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) {
|
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;
|
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->domain_id = id->domain_id;
|
||||||
adev->version = id->svc_version;
|
adev->version = id->svc_version;
|
||||||
if (np)
|
if (np)
|
||||||
strncpy(adev->name, np->name, APR_NAME_SIZE);
|
strscpy(adev->name, np->name, APR_NAME_SIZE);
|
||||||
else
|
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,
|
dev_set_name(&adev->dev, "aprsvc:%s:%x:%x", adev->name,
|
||||||
id->domain_id, id->svc_id);
|
id->domain_id, id->svc_id);
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include <linux/mutex.h>
|
#include <linux/mutex.h>
|
||||||
#include <linux/of_device.h>
|
#include <linux/of_device.h>
|
||||||
#include <linux/regmap.h>
|
#include <linux/regmap.h>
|
||||||
|
#include <linux/sizes.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/soc/qcom/llcc-qcom.h>
|
#include <linux/soc/qcom/llcc-qcom.h>
|
||||||
|
|
||||||
|
@ -106,22 +107,24 @@ static int llcc_update_act_ctrl(u32 sid,
|
||||||
u32 slice_status;
|
u32 slice_status;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
act_ctrl_reg = drv_data->bcast_off + LLCC_TRP_ACT_CTRLn(sid);
|
act_ctrl_reg = LLCC_TRP_ACT_CTRLn(sid);
|
||||||
status_reg = drv_data->bcast_off + LLCC_TRP_STATUSn(sid);
|
status_reg = LLCC_TRP_STATUSn(sid);
|
||||||
|
|
||||||
/* Set the ACTIVE trigger */
|
/* Set the ACTIVE trigger */
|
||||||
act_ctrl_reg_val |= ACT_CTRL_ACT_TRIG;
|
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)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
/* Clear the ACTIVE trigger */
|
/* Clear the ACTIVE trigger */
|
||||||
act_ctrl_reg_val &= ~ACT_CTRL_ACT_TRIG;
|
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)
|
if (ret)
|
||||||
return 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),
|
slice_status, !(slice_status & status),
|
||||||
0, LLCC_STATUS_READ_DELAY);
|
0, LLCC_STATUS_READ_DELAY);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -223,19 +226,16 @@ static int qcom_llcc_cfg_program(struct platform_device *pdev)
|
||||||
u32 attr0_val;
|
u32 attr0_val;
|
||||||
u32 max_cap_cacheline;
|
u32 max_cap_cacheline;
|
||||||
u32 sz;
|
u32 sz;
|
||||||
int ret;
|
int ret = 0;
|
||||||
const struct llcc_slice_config *llcc_table;
|
const struct llcc_slice_config *llcc_table;
|
||||||
struct llcc_slice_desc desc;
|
struct llcc_slice_desc desc;
|
||||||
u32 bcast_off = drv_data->bcast_off;
|
|
||||||
|
|
||||||
sz = drv_data->cfg_size;
|
sz = drv_data->cfg_size;
|
||||||
llcc_table = drv_data->cfg;
|
llcc_table = drv_data->cfg;
|
||||||
|
|
||||||
for (i = 0; i < sz; i++) {
|
for (i = 0; i < sz; i++) {
|
||||||
attr1_cfg = bcast_off +
|
attr1_cfg = LLCC_TRP_ATTR1_CFGn(llcc_table[i].slice_id);
|
||||||
LLCC_TRP_ATTR1_CFGn(llcc_table[i].slice_id);
|
attr0_cfg = LLCC_TRP_ATTR0_CFGn(llcc_table[i].slice_id);
|
||||||
attr0_cfg = bcast_off +
|
|
||||||
LLCC_TRP_ATTR0_CFGn(llcc_table[i].slice_id);
|
|
||||||
|
|
||||||
attr1_val = llcc_table[i].cache_mode;
|
attr1_val = llcc_table[i].cache_mode;
|
||||||
attr1_val |= llcc_table[i].probe_target_ways <<
|
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].res_ways & ATTR0_RES_WAYS_MASK;
|
||||||
attr0_val |= llcc_table[i].bonus_ways << ATTR0_BONUS_WAYS_SHIFT;
|
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)
|
if (ret)
|
||||||
return 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)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
if (llcc_table[i].activate_on_init) {
|
if (llcc_table[i].activate_on_init) {
|
||||||
|
@ -279,24 +281,37 @@ int qcom_llcc_probe(struct platform_device *pdev,
|
||||||
{
|
{
|
||||||
u32 num_banks;
|
u32 num_banks;
|
||||||
struct device *dev = &pdev->dev;
|
struct device *dev = &pdev->dev;
|
||||||
struct resource *res;
|
struct resource *llcc_banks_res, *llcc_bcast_res;
|
||||||
void __iomem *base;
|
void __iomem *llcc_banks_base, *llcc_bcast_base;
|
||||||
int ret, i;
|
int ret, i;
|
||||||
|
struct platform_device *llcc_edac;
|
||||||
|
|
||||||
drv_data = devm_kzalloc(dev, sizeof(*drv_data), GFP_KERNEL);
|
drv_data = devm_kzalloc(dev, sizeof(*drv_data), GFP_KERNEL);
|
||||||
if (!drv_data)
|
if (!drv_data)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
llcc_banks_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
||||||
base = devm_ioremap_resource(&pdev->dev, res);
|
"llcc_base");
|
||||||
if (IS_ERR(base))
|
llcc_banks_base = devm_ioremap_resource(&pdev->dev, llcc_banks_res);
|
||||||
return PTR_ERR(base);
|
if (IS_ERR(llcc_banks_base))
|
||||||
|
return PTR_ERR(llcc_banks_base);
|
||||||
|
|
||||||
drv_data->regmap = devm_regmap_init_mmio(dev, base,
|
drv_data->regmap = devm_regmap_init_mmio(dev, llcc_banks_base,
|
||||||
&llcc_regmap_config);
|
&llcc_regmap_config);
|
||||||
if (IS_ERR(drv_data->regmap))
|
if (IS_ERR(drv_data->regmap))
|
||||||
return PTR_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,
|
ret = regmap_read(drv_data->regmap, LLCC_COMMON_STATUS0,
|
||||||
&num_banks);
|
&num_banks);
|
||||||
if (ret)
|
if (ret)
|
||||||
|
@ -318,8 +333,6 @@ int qcom_llcc_probe(struct platform_device *pdev,
|
||||||
for (i = 0; i < num_banks; i++)
|
for (i = 0; i < num_banks; i++)
|
||||||
drv_data->offsets[i] = i * BANK_OFFSET_STRIDE;
|
drv_data->offsets[i] = i * BANK_OFFSET_STRIDE;
|
||||||
|
|
||||||
drv_data->bcast_off = num_banks * BANK_OFFSET_STRIDE;
|
|
||||||
|
|
||||||
drv_data->bitmap = devm_kcalloc(dev,
|
drv_data->bitmap = devm_kcalloc(dev,
|
||||||
BITS_TO_LONGS(drv_data->max_slices), sizeof(unsigned long),
|
BITS_TO_LONGS(drv_data->max_slices), sizeof(unsigned long),
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
|
@ -331,7 +344,20 @@ int qcom_llcc_probe(struct platform_device *pdev,
|
||||||
mutex_init(&drv_data->lock);
|
mutex_init(&drv_data->lock);
|
||||||
platform_set_drvdata(pdev, drv_data);
|
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);
|
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)
|
int geni_se_clk_tbl_get(struct geni_se *se, unsigned long **tbl)
|
||||||
{
|
{
|
||||||
unsigned long freq = 0;
|
long freq = 0;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (se->clk_perf_tbl) {
|
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++) {
|
for (i = 0; i < MAX_CLK_PERF_LEVEL; i++) {
|
||||||
freq = clk_round_rate(se->clk, freq + 1);
|
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;
|
break;
|
||||||
se->clk_perf_tbl[i] = freq;
|
se->clk_perf_tbl[i] = freq;
|
||||||
}
|
}
|
||||||
|
@ -544,16 +544,17 @@ EXPORT_SYMBOL(geni_se_clk_tbl_get);
|
||||||
* @se: Pointer to the concerned serial engine.
|
* @se: Pointer to the concerned serial engine.
|
||||||
* @req_freq: Requested clock frequency.
|
* @req_freq: Requested clock frequency.
|
||||||
* @index: Index of the resultant frequency in the table.
|
* @index: Index of the resultant frequency in the table.
|
||||||
* @res_freq: Resultant frequency which matches or is closer to the
|
* @res_freq: Resultant frequency of the source clock.
|
||||||
* requested frequency.
|
|
||||||
* @exact: Flag to indicate exact multiple requirement of the requested
|
* @exact: Flag to indicate exact multiple requirement of the requested
|
||||||
* frequency.
|
* frequency.
|
||||||
*
|
*
|
||||||
* This function is called by the protocol drivers to determine the matching
|
* This function is called by the protocol drivers to determine the best match
|
||||||
* or exact multiple of the requested frequency, as provided by the serial
|
* of the requested frequency as provided by the serial engine clock in order
|
||||||
* engine clock in order to meet the performance requirements. If there is
|
* to meet the performance requirements.
|
||||||
* no matching or exact multiple of the requested frequency found, then it
|
*
|
||||||
* selects the closest floor frequency, if exact flag is not set.
|
* 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.
|
* 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;
|
unsigned long *tbl;
|
||||||
int num_clk_levels;
|
int num_clk_levels;
|
||||||
int i;
|
int i;
|
||||||
|
unsigned long best_delta;
|
||||||
|
unsigned long new_delta;
|
||||||
|
unsigned int divider;
|
||||||
|
|
||||||
num_clk_levels = geni_se_clk_tbl_get(se, &tbl);
|
num_clk_levels = geni_se_clk_tbl_get(se, &tbl);
|
||||||
if (num_clk_levels < 0)
|
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)
|
if (num_clk_levels == 0)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
*res_freq = 0;
|
best_delta = ULONG_MAX;
|
||||||
for (i = 0; i < num_clk_levels; i++) {
|
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;
|
*index = i;
|
||||||
*res_freq = tbl[i];
|
*res_freq = tbl[i];
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(*res_freq) || ((tbl[i] > *res_freq) &&
|
/* If the new best is exact then we're done */
|
||||||
(tbl[i] < req_freq))) {
|
if (new_delta == 0)
|
||||||
*index = i;
|
return 0;
|
||||||
*res_freq = tbl[i];
|
|
||||||
|
/* 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");
|
dev_err(&pdev->dev, "failed to parse qcom,vmid\n");
|
||||||
goto remove_cdev;
|
goto remove_cdev;
|
||||||
} else if (!ret) {
|
} else if (!ret) {
|
||||||
|
if (!qcom_scm_is_available()) {
|
||||||
|
ret = -EPROBE_DEFER;
|
||||||
|
goto remove_cdev;
|
||||||
|
}
|
||||||
|
|
||||||
perms[0].vmid = QCOM_SCM_VMID_HLOS;
|
perms[0].vmid = QCOM_SCM_VMID_HLOS;
|
||||||
perms[0].perm = QCOM_SCM_PERM_RW;
|
perms[0].perm = QCOM_SCM_PERM_RW;
|
||||||
perms[1].vmid = vmid;
|
perms[1].vmid = vmid;
|
||||||
|
|
|
@ -121,6 +121,7 @@ static int tcs_invalidate(struct rsc_drv *drv, int type)
|
||||||
return -EAGAIN;
|
return -EAGAIN;
|
||||||
}
|
}
|
||||||
write_tcs_reg_sync(drv, RSC_DRV_CMD_ENABLE, m, 0);
|
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);
|
bitmap_zero(tcs->slots, MAX_TCS_SLOTS);
|
||||||
spin_unlock(&tcs->lock);
|
spin_unlock(&tcs->lock);
|
||||||
|
@ -239,6 +240,7 @@ static irqreturn_t tcs_tx_done(int irq, void *p)
|
||||||
skip:
|
skip:
|
||||||
/* Reclaim the TCS */
|
/* Reclaim the TCS */
|
||||||
write_tcs_reg(drv, RSC_DRV_CMD_ENABLE, i, 0);
|
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));
|
write_tcs_reg(drv, RSC_DRV_IRQ_CLEAR, 0, BIT(i));
|
||||||
spin_lock(&drv->lock);
|
spin_lock(&drv->lock);
|
||||||
clear_bit(i, drv->tcs_in_use);
|
clear_bit(i, drv->tcs_in_use);
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_address.h>
|
#include <linux/of_address.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/sizes.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/soc/qcom/smem.h>
|
#include <linux/soc/qcom/smem.h>
|
||||||
|
|
||||||
|
@ -277,7 +278,7 @@ struct qcom_smem {
|
||||||
u32 item_count;
|
u32 item_count;
|
||||||
|
|
||||||
unsigned num_regions;
|
unsigned num_regions;
|
||||||
struct smem_region regions[0];
|
struct smem_region regions[];
|
||||||
};
|
};
|
||||||
|
|
||||||
static void *
|
static void *
|
||||||
|
@ -489,7 +490,7 @@ static void *qcom_smem_get_global(struct qcom_smem *smem,
|
||||||
size_t *size)
|
size_t *size)
|
||||||
{
|
{
|
||||||
struct smem_header *header;
|
struct smem_header *header;
|
||||||
struct smem_region *area;
|
struct smem_region *region;
|
||||||
struct smem_global_entry *entry;
|
struct smem_global_entry *entry;
|
||||||
u32 aux_base;
|
u32 aux_base;
|
||||||
unsigned i;
|
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;
|
aux_base = le32_to_cpu(entry->aux_base) & AUX_BASE_MASK;
|
||||||
|
|
||||||
for (i = 0; i < smem->num_regions; i++) {
|
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)
|
if (size != NULL)
|
||||||
*size = le32_to_cpu(entry->size);
|
*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);
|
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)
|
static int qcom_smem_set_global_partition(struct qcom_smem *smem)
|
||||||
{
|
{
|
||||||
struct smem_partition_header *header;
|
struct smem_partition_header *header;
|
||||||
struct smem_ptable_entry *entry;
|
struct smem_ptable_entry *entry;
|
||||||
struct smem_ptable *ptable;
|
struct smem_ptable *ptable;
|
||||||
u32 host0, host1, size;
|
|
||||||
bool found = false;
|
bool found = false;
|
||||||
int i;
|
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++) {
|
for (i = 0; i < le32_to_cpu(ptable->num_entries); i++) {
|
||||||
entry = &ptable->entry[i];
|
entry = &ptable->entry[i];
|
||||||
host0 = le16_to_cpu(entry->host0);
|
if (!le32_to_cpu(entry->offset))
|
||||||
host1 = le16_to_cpu(entry->host1);
|
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;
|
found = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -756,36 +809,10 @@ static int qcom_smem_set_global_partition(struct qcom_smem *smem)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!le32_to_cpu(entry->offset) || !le32_to_cpu(entry->size)) {
|
header = qcom_smem_partition_header(smem, entry,
|
||||||
dev_err(smem->dev, "Invalid entry for global partition\n");
|
SMEM_GLOBAL_HOST, SMEM_GLOBAL_HOST);
|
||||||
|
if (!header)
|
||||||
return -EINVAL;
|
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_partition = header;
|
||||||
smem->global_cacheline = le32_to_cpu(entry->cacheline);
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int qcom_smem_enumerate_partitions(struct qcom_smem *smem,
|
static int
|
||||||
unsigned int local_host)
|
qcom_smem_enumerate_partitions(struct qcom_smem *smem, u16 local_host)
|
||||||
{
|
{
|
||||||
struct smem_partition_header *header;
|
struct smem_partition_header *header;
|
||||||
struct smem_ptable_entry *entry;
|
struct smem_ptable_entry *entry;
|
||||||
struct smem_ptable *ptable;
|
struct smem_ptable *ptable;
|
||||||
unsigned int remote_host;
|
unsigned int remote_host;
|
||||||
u32 host0, host1;
|
u16 host0, host1;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
ptable = qcom_smem_get_ptable(smem);
|
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++) {
|
for (i = 0; i < le32_to_cpu(ptable->num_entries); i++) {
|
||||||
entry = &ptable->entry[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))
|
if (!le32_to_cpu(entry->offset))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!le32_to_cpu(entry->size))
|
if (!le32_to_cpu(entry->size))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
host0 = le16_to_cpu(entry->host0);
|
||||||
|
host1 = le16_to_cpu(entry->host1);
|
||||||
if (host0 == local_host)
|
if (host0 == local_host)
|
||||||
remote_host = host1;
|
remote_host = host1;
|
||||||
else
|
else if (host1 == local_host)
|
||||||
remote_host = host0;
|
remote_host = host0;
|
||||||
|
else
|
||||||
|
continue;
|
||||||
|
|
||||||
if (remote_host >= SMEM_HOST_COUNT) {
|
if (remote_host >= SMEM_HOST_COUNT) {
|
||||||
dev_err(smem->dev,
|
dev_err(smem->dev, "bad host %hu\n", remote_host);
|
||||||
"Invalid remote host %d\n",
|
|
||||||
remote_host);
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (smem->partitions[remote_host]) {
|
if (smem->partitions[remote_host]) {
|
||||||
dev_err(smem->dev,
|
dev_err(smem->dev, "duplicate host %hu\n", remote_host);
|
||||||
"Already found a partition for host %d\n",
|
|
||||||
remote_host);
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
header = smem->regions[0].virt_base + le32_to_cpu(entry->offset);
|
header = qcom_smem_partition_header(smem, entry, host0, host1);
|
||||||
host0 = le16_to_cpu(header->host0);
|
if (!header)
|
||||||
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);
|
|
||||||
return -EINVAL;
|
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->partitions[remote_host] = header;
|
||||||
smem->cacheline[remote_host] = le32_to_cpu(entry->cacheline);
|
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 device_node *np;
|
||||||
struct resource r;
|
struct resource r;
|
||||||
|
resource_size_t size;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
np = of_parse_phandle(dev->of_node, name, 0);
|
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);
|
of_node_put(np);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
size = resource_size(&r);
|
||||||
|
|
||||||
smem->regions[i].aux_base = (u32)r.start;
|
smem->regions[i].virt_base = devm_ioremap_wc(dev, r.start, size);
|
||||||
smem->regions[i].size = resource_size(&r);
|
|
||||||
smem->regions[i].virt_base = devm_ioremap_wc(dev, r.start, resource_size(&r));
|
|
||||||
if (!smem->regions[i].virt_base)
|
if (!smem->regions[i].virt_base)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
smem->regions[i].aux_base = (u32)r.start;
|
||||||
|
smem->regions[i].size = size;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -962,6 +953,7 @@ static int qcom_smem_probe(struct platform_device *pdev)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BUILD_BUG_ON(SMEM_HOST_APPS >= SMEM_HOST_COUNT);
|
||||||
ret = qcom_smem_enumerate_partitions(smem, SMEM_HOST_APPS);
|
ret = qcom_smem_enumerate_partitions(smem, SMEM_HOST_APPS);
|
||||||
if (ret < 0 && ret != -ENOENT)
|
if (ret < 0 && ret != -ENOENT)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
|
@ -219,6 +219,9 @@ static int __init qcom_cpuidle_init(struct device_node *cpu_node, int cpu)
|
||||||
cpumask_t mask;
|
cpumask_t mask;
|
||||||
bool use_scm_power_down = false;
|
bool use_scm_power_down = false;
|
||||||
|
|
||||||
|
if (!qcom_scm_is_available())
|
||||||
|
return -EPROBE_DEFER;
|
||||||
|
|
||||||
for (i = 0; ; i++) {
|
for (i = 0; ; i++) {
|
||||||
state_node = of_parse_phandle(cpu_node, "cpu-idle-states", i);
|
state_node = of_parse_phandle(cpu_node, "cpu-idle-states", i);
|
||||||
if (!state_node)
|
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 rpmsg_channel_info chinfo;
|
||||||
struct wcnss_ctrl *_wcnss = wcnss;
|
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.src = RPMSG_ADDR_ANY;
|
||||||
chinfo.dst = RPMSG_ADDR_ANY;
|
chinfo.dst = RPMSG_ADDR_ANY;
|
||||||
|
|
||||||
|
|
|
@ -225,19 +225,14 @@ struct geni_se {
|
||||||
#define HW_VER_MINOR_SHFT 16
|
#define HW_VER_MINOR_SHFT 16
|
||||||
#define HW_VER_STEP_MASK GENMASK(15, 0)
|
#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)
|
#if IS_ENABLED(CONFIG_QCOM_GENI_SE)
|
||||||
|
|
||||||
u32 geni_se_get_qup_hw_version(struct geni_se *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
|
* geni_se_read_proto() - Read the protocol configured for a serial engine
|
||||||
* @se: Pointer to the concerned 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
|
* llcc_drv_data - Data associated with the llcc driver
|
||||||
* @regmap: regmap associated with the llcc device
|
* @regmap: regmap associated with the llcc device
|
||||||
|
* @bcast_regmap: regmap associated with llcc broadcast offset
|
||||||
* @cfg: pointer to the data structure for slice configuration
|
* @cfg: pointer to the data structure for slice configuration
|
||||||
* @lock: mutex associated with each slice
|
* @lock: mutex associated with each slice
|
||||||
* @cfg_size: size of the config data table
|
* @cfg_size: size of the config data table
|
||||||
* @max_slices: max slices as read from device tree
|
* @max_slices: max slices as read from device tree
|
||||||
* @bcast_off: Offset of the broadcast bank
|
|
||||||
* @num_banks: Number of llcc banks
|
* @num_banks: Number of llcc banks
|
||||||
* @bitmap: Bit map to track the active slice ids
|
* @bitmap: Bit map to track the active slice ids
|
||||||
* @offsets: Pointer to the bank offsets array
|
* @offsets: Pointer to the bank offsets array
|
||||||
|
* @ecc_irq: interrupt for llcc cache error detection and reporting
|
||||||
*/
|
*/
|
||||||
struct llcc_drv_data {
|
struct llcc_drv_data {
|
||||||
struct regmap *regmap;
|
struct regmap *regmap;
|
||||||
|
struct regmap *bcast_regmap;
|
||||||
const struct llcc_slice_config *cfg;
|
const struct llcc_slice_config *cfg;
|
||||||
struct mutex lock;
|
struct mutex lock;
|
||||||
u32 cfg_size;
|
u32 cfg_size;
|
||||||
u32 max_slices;
|
u32 max_slices;
|
||||||
u32 bcast_off;
|
|
||||||
u32 num_banks;
|
u32 num_banks;
|
||||||
unsigned long *bitmap;
|
unsigned long *bitmap;
|
||||||
u32 *offsets;
|
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)
|
#if IS_ENABLED(CONFIG_QCOM_LLCC)
|
||||||
|
|
Loading…
Reference in New Issue