This series implements system cache or LLCC(Last Level Cache Controller) driver
for SDM845 SOC. The purpose of the driver is to partition the system cache and
program the settings such as priortiy, lines to probe while doing a look up
in the system cache, low power related settings etc. The partitions are called
cache slices. Each cache slice is associated with size and SCID(System Cache ID)
The driver also provides API for clients to query the cache slice details,
activate and deactivate them.
The driver can be broadly classified into:
* SOC specific driver: llcc-sdm845.c: Cache partitioning and cache slice
properties for usecases on sdm845 that need to use system cache.
* API : llcc-slice.c: Exports APIs to clients to query cache slice details,
activate and deactivate cache slices. Register the child devices using platform
APIs.
Changes since v1:
* Added Makefile and Kconfig.
[email protected] (2):
Documentation: Documentaion for qcom, llcc
drivers: soc: Add LLCC driver
.../devicetree/bindings/arm/msm/qcom,llcc.txt | 70 ++++
drivers/soc/qcom/Kconfig | 16 +
drivers/soc/qcom/Makefile | 2 +
drivers/soc/qcom/llcc-sdm845.c | 120 ++++++
drivers/soc/qcom/llcc-slice.c | 454 +++++++++++++++++++++
include/linux/soc/qcom/llcc-qcom.h | 178 ++++++++
6 files changed, 840 insertions(+)
create mode 100644 Documentation/devicetree/bindings/arm/msm/qcom,llcc.txt
create mode 100644 drivers/soc/qcom/llcc-sdm845.c
create mode 100644 drivers/soc/qcom/llcc-slice.c
create mode 100644 include/linux/soc/qcom/llcc-qcom.h
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
Documentation for last level cache controller device tree bindings,
client bindings usage examples.
Change-Id: Ic2d6d6154ab8269cfce6828e9f2250320a0572e8
Signed-off-by: Channagoud Kadabi <[email protected]>
Signed-off-by: Rishabh Bhatnagar <[email protected]>
---
.../devicetree/bindings/arm/msm/qcom,llcc.txt | 70 ++++++++++++++++++++++
1 file changed, 70 insertions(+)
create mode 100644 Documentation/devicetree/bindings/arm/msm/qcom,llcc.txt
diff --git a/Documentation/devicetree/bindings/arm/msm/qcom,llcc.txt b/Documentation/devicetree/bindings/arm/msm/qcom,llcc.txt
new file mode 100644
index 0000000..ceb20a4
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/msm/qcom,llcc.txt
@@ -0,0 +1,70 @@
+== Introduction==
+
+LLCC (Last Level Cache Controller) driver is implemented as a platform device.
+The driver Programs the SCT (system configuration table). The SCT programming
+divides the system cache into slices. Each slice is assigned an ID A.K.A
+SCID(Sub-cache ID).
+HW modules that are designated to use the system cache are known as clients.
+Each client must also be represented as a node in the device tree just like
+any other hw module.
+One client can have multiple SCID's assigned meaning each client could get
+multiple slices in the cache. Client can use the slices for various pre-defined
+usecases. Each client defines a set of names for these usecases in its
+device tree binding.
+Client makes a request to LLCC device to get cache-slices properties for each
+of its usecase. Client gets the information like cache slice ID and size of the
+cache slice.
+
+== llcc device ==
+
+Properties:
+- compatible:
+ Usage: required
+ Value type: <string>
+ Definition: must be "qcom,sdm855-llcc"
+
+- reg:
+ Usage: required
+ Value Type: <prop-encoded-array>
+ Definition: must be addresses and sizes of the LLCC registers
+
+- #cache-cells:
+ Usage: required
+ Value Type: <u32>
+ Definition: Number of cache cells, must be 1
+
+- max-slices:
+ usage: required
+ Value Type: <u32>
+ Definition: Number of cache slices supported by hardware
+
+Example:
+
+ llcc: qcom,sdm855-llcc@01100000 {
+ compatible = "qcom,sdm845-llcc";
+ reg = <0x01100000 0x250000>;
+ #cache-cells = <1>;
+ max-slices = <32>;
+ };
+
+== Client ==
+
+Required properties:
+- cache-slice-names:
+ Usage: required
+ Value type: <stringlist>
+ Definition: A set of names that identify the usecase names of a client that
+ uses cache slice. These strings are used to look up the cache slice
+ entries by name.
+
+- cache-slices:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: The tuple has phandle to llcc device as the first argument and
+ the second argument is the usecase id of the client.
+For Example:
+
+ venus {
+ cache-slice-names = "vidsc0", "vidsc1";
+ cache-slices = <&llcc 2>, <&llcc 3>;
+ };
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
LLCC (Last Level Cache Controller) provides additional cache memory
in the system. LLCC is partitioned into muliple slices and each
slice getting its own priority, size, ID and other config parameters.
LLCC driver programs these parameters for each slice. Clients that
are assigned to use LLCC need to get information such size & ID of the
slice they get and activate or deactivate the slice as needed. LLCC driver
provides API interfaces for the clients to perform these operations.
Change-Id: I93c5f3225d0a917f0d6c64c7588a4e64e33a59ed
Signed-off-by: Channagoud Kadabi <[email protected]>
Signed-off-by: Rishabh Bhatnagar <[email protected]>
---
drivers/soc/qcom/Kconfig | 16 ++
drivers/soc/qcom/Makefile | 2 +
drivers/soc/qcom/llcc-sdm845.c | 120 ++++++++++
drivers/soc/qcom/llcc-slice.c | 454 +++++++++++++++++++++++++++++++++++++
include/linux/soc/qcom/llcc-qcom.h | 178 +++++++++++++++
5 files changed, 770 insertions(+)
create mode 100644 drivers/soc/qcom/llcc-sdm845.c
create mode 100644 drivers/soc/qcom/llcc-slice.c
create mode 100644 include/linux/soc/qcom/llcc-qcom.h
diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
index e050eb8..28237fc 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -21,6 +21,22 @@ config QCOM_GSBI
functions for connecting the underlying serial UART, SPI, and I2C
devices to the output pins.
+config QCOM_LLCC
+ tristate "Qualcomm Technologies, Inc. LLCC driver"
+ depends on ARCH_QCOM
+ help
+ Qualcomm Technologies, Inc. platform specific LLCC driver for Last
+ Level Cache. This provides interfaces to client's that use the LLCC.
+ Say yes here to enable LLCC slice driver.
+
+config QCOM_SDM845_LLCC
+ tristate "Qualcomm Technologies, Inc. SDM845 LLCC driver"
+ depends on QCOM_LLCC
+ help
+ Say yes here to enable the LLCC driver for SDM845. This is provides
+ data required to configure LLCC so that clients can start using the
+ LLCC slices.
+
config QCOM_MDT_LOADER
tristate
select QCOM_SCM
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index dcebf28..234560f 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -12,3 +12,5 @@ obj-$(CONFIG_QCOM_SMEM_STATE) += smem_state.o
obj-$(CONFIG_QCOM_SMP2P) += smp2p.o
obj-$(CONFIG_QCOM_SMSM) += smsm.o
obj-$(CONFIG_QCOM_WCNSS_CTRL) += wcnss_ctrl.o
+obj-$(CONFIG_QCOM_LLCC) += llcc-core.o llcc-slice.o
+obj-$(CONFIG_QCOM_SDM845_LLCC) += llcc-sdm845.o
diff --git a/drivers/soc/qcom/llcc-sdm845.c b/drivers/soc/qcom/llcc-sdm845.c
new file mode 100644
index 0000000..cd431d9
--- /dev/null
+++ b/drivers/soc/qcom/llcc-sdm845.c
@@ -0,0 +1,120 @@
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/soc/qcom/llcc-qcom.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+
+
+/*
+ * SCT entry contains of the following parameters
+ * name: Name of the client's use case for which the llcc slice is used
+ * uid: Unique id for the client's use case
+ * slice_id: llcc slice id for each client
+ * max_cap: The maximum capacity of the cache slice provided in KB
+ * priority: Priority of the client used to select victim line for replacement
+ * fixed_size: Determine of the slice has a fixed capacity
+ * bonus_ways: Bonus ways to be used by any slice, bonus way is used only if
+ * it't not a reserved way.
+ * res_ways: Reserved ways for the cache slice, the reserved ways cannot be
+ * used by any other client than the one its assigned to.
+ * cache_mode: Each slice operates as a cache, this controls the mode of the
+ * slice normal or TCM
+ * probe_target_ways: Determines what ways to probe for access hit. When
+ * configured to 1 only bonus and reseved ways are probed.
+ * when configured to 0 all ways in llcc are probed.
+ * dis_cap_alloc: Disable capacity based allocation for a client
+ * retain_on_pc: If this bit is set and client has maitained active vote
+ * then the ways assigned to this client are not flushed on power
+ * collapse.
+ * activate_on_init: Activate the slice immidiately after the SCT is programmed
+ */
+#define SCT_ENTRY(n, uid, sid, mc, p, fs, bway, rway, cmod, ptw, dca, rp, a) \
+ { \
+ .name = n, \
+ .usecase_id = uid, \
+ .slice_id = sid, \
+ .max_cap = mc, \
+ .priority = p, \
+ .fixed_size = fs, \
+ .bonus_ways = bway, \
+ .res_ways = rway, \
+ .cache_mode = cmod, \
+ .probe_target_ways = ptw, \
+ .dis_cap_alloc = dca, \
+ .retain_on_pc = rp, \
+ .activate_on_init = a, \
+ }
+
+
+static struct llcc_slice_config sdm845_data[] = {
+ SCT_ENTRY("cpuss", 1, 1, 2816, 1, 0, 0xFFC, 0x2, 0, 0, 1, 1, 1),
+ SCT_ENTRY("vidsc0", 2, 2, 512, 2, 1, 0x0, 0x0F0, 0, 0, 1, 1, 0),
+ SCT_ENTRY("vidsc1", 3, 3, 512, 2, 1, 0x0, 0x0F0, 0, 0, 1, 1, 0),
+ SCT_ENTRY("rotator", 4, 4, 563, 2, 1, 0x0, 0x00e, 2, 0, 1, 1, 0),
+ SCT_ENTRY("voice", 5, 5, 2816, 1, 0, 0xFFC, 0x2, 0, 0, 1, 1, 0),
+ SCT_ENTRY("audio", 6, 6, 2816, 1, 0, 0xFFC, 0x2, 0, 0, 1, 1, 0),
+ SCT_ENTRY("modemhp_grow", 7, 7, 1024, 2, 0, 0x0FC, 0xF00, 0, 0, 1, 1, 0),
+ SCT_ENTRY("modem", 8, 8, 2816, 1, 0, 0xFFC, 0x2, 0, 0, 1, 1, 0),
+ SCT_ENTRY("compute", 10, 10, 2816, 1, 0, 0xFFC, 0x2, 0, 0, 1, 1, 0),
+ SCT_ENTRY("gpuhtw", 11, 11, 512, 1, 1, 0xC, 0x0, 0, 0, 1, 1, 0),
+ SCT_ENTRY("gpu", 12, 12, 2304, 1, 0, 0xFF0, 0x2, 0, 0, 1, 1, 0),
+ SCT_ENTRY("mmuhwt", 13, 13, 256, 2, 0, 0x0, 0x1, 0, 0, 1, 0, 1),
+ SCT_ENTRY("compute_dma", 15, 15, 2816, 1, 0, 0xFFC, 0x2, 0, 0, 1, 1, 0),
+ SCT_ENTRY("display", 16, 16, 2816, 1, 0, 0xFFC, 0x2, 0, 0, 1, 1, 0),
+ SCT_ENTRY("videofw", 17, 17, 2816, 1, 0, 0xFFC, 0x2, 0, 0, 1, 1, 0),
+ SCT_ENTRY("modemhp_fix", 20, 20, 1024, 2, 1, 0x0, 0xF00, 0, 0, 1, 1, 0),
+ SCT_ENTRY("modem_paging", 21, 21, 1024, 0, 1, 0x1e, 0x0, 0, 0, 1, 1, 0),
+ SCT_ENTRY("audiohw", 22, 22, 1024, 1, 1, 0xFFC, 0x2, 0, 0, 1, 1, 0),
+};
+
+
+static int sdm845_qcom_llcc_probe(struct platform_device *pdev)
+{
+ return qcom_llcc_probe(pdev, sdm845_data,ARRAY_SIZE(sdm845_data));
+}
+
+static const struct of_device_id sdm845_qcom_llcc_of_match[] = {
+ { .compatible = "qcom,sdm845-llcc", },
+ { },
+};
+
+static struct platform_driver sdm845_qcom_llcc_driver = {
+ .driver = {
+ .name = "sdm845-llcc",
+ .owner = THIS_MODULE,
+ .of_match_table = sdm845_qcom_llcc_of_match,
+ },
+ .probe = sdm845_qcom_llcc_probe,
+ .remove = qcom_llcc_remove,
+};
+
+static int __init sdm845_init_qcom_llcc_init(void)
+{
+ return platform_driver_register(&sdm845_qcom_llcc_driver);
+}
+module_init(sdm845_init_qcom_llcc_init);
+
+static void __exit sdm845_exit_qcom_llcc_exit(void)
+{
+ platform_driver_unregister(&sdm845_qcom_llcc_driver);
+}
+module_exit(sdm845_exit_qcom_llcc_exit);
+
+MODULE_DESCRIPTION("QTI sdm845 LLCC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/soc/qcom/llcc-slice.c b/drivers/soc/qcom/llcc-slice.c
new file mode 100644
index 0000000..6e86770
--- /dev/null
+++ b/drivers/soc/qcom/llcc-slice.c
@@ -0,0 +1,454 @@
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s:" fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/bitmap.h>
+#include <linux/bitops.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#include <linux/soc/qcom/llcc-qcom.h>
+
+#define ACTIVATE 0x1
+#define DEACTIVATE 0x2
+#define ACT_CTRL_OPCODE_ACTIVATE 0x1
+#define ACT_CTRL_OPCODE_DEACTIVATE 0x2
+#define ACT_CTRL_ACT_TRIG 0x1
+#define ACT_CTRL_OPCODE_SHIFT 0x1
+#define ATTR1_PROBE_TARGET_WAYS_SHIFT 0x2
+#define ATTR1_FIXED_SIZE_SHIFT 0x3
+#define ATTR1_PRIORITY_SHIFT 0x4
+#define ATTR1_MAX_CAP_SHIFT 0x10
+#define ATTR0_RES_WAYS_MASK 0x00000fff
+#define ATR0_BONUS_WAYS_MASK 0x0fff0000
+#define ATR0_BONUS_WAYS_SHIFT 0x10
+#define LLCC_STATUS_READ_DELAY 100
+
+#define CACHE_LINE_SIZE_SHIFT 6
+
+#define LLCC_COMMON_STATUS0 0x0003000C
+#define LLCC_LB_CNT_MASK 0xf0000000
+#define LLCC_LB_CNT_SHIFT 28
+
+#define MAX_CAP_TO_BYTES(n) (n * 1024)
+#define LLCC_TRP_ACT_CTRLn(n) (n * 0x1000)
+#define LLCC_TRP_STATUSn(n) (4 + n * 0x1000)
+#define LLCC_TRP_ATTR0_CFGn(n) (0x21000 + 0x8 * n)
+#define LLCC_TRP_ATTR1_CFGn(n) (0x21004 + 0x8 * n)
+
+
+
+static const struct regmap_config llcc_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .fast_io = true,
+};
+
+
+/* Get the slice entry by index */
+static struct llcc_slice_desc *llcc_slice_get_entry(struct device *dev, int n)
+{
+ struct of_phandle_args phargs;
+ struct llcc_drv_data *drv;
+ const struct llcc_slice_config *llcc_data_ptr;
+ struct llcc_slice_desc *desc;
+ struct platform_device *pdev;
+ u32 sz, count;
+
+ if (of_parse_phandle_with_args(dev->of_node, "cache-slices",
+ "#cache-cells", n, &phargs)) {
+ pr_err("can't parse \"cache-slices\" property\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ pdev = of_find_device_by_node(phargs.np);
+ if (!pdev) {
+ pr_err("Cannot find platform device from phandle\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ drv = platform_get_drvdata(pdev);
+ if (!drv) {
+ pr_err("cannot find platform driver data\n");
+ return ERR_PTR(-EFAULT);
+ }
+
+ llcc_data_ptr = drv->slice_data;
+ sz = drv->llcc_config_data_sz;
+ count = 0;
+
+ while (llcc_data_ptr && count < sz) {
+ if (llcc_data_ptr->usecase_id == phargs.args[0])
+ break;
+ llcc_data_ptr++;
+ count++;
+ }
+
+ if (llcc_data_ptr == NULL || count == sz) {
+ pr_err("can't find %d usecase id\n", phargs.args[0]);
+ return ERR_PTR(-ENODEV);
+ }
+
+ desc = kzalloc(sizeof(struct llcc_slice_desc), GFP_KERNEL);
+ if (!desc)
+ return ERR_PTR(-ENOMEM);
+
+ desc->llcc_slice_id = llcc_data_ptr->slice_id;
+ desc->llcc_slice_size = llcc_data_ptr->max_cap;
+ desc->dev = &pdev->dev;
+
+ return desc;
+}
+
+/**
+ * llcc_slice_getd - get llcc slice descriptor
+ * @dev: Device pointer of the client
+ * @name: Name of the use case
+ *
+ * A pointer to llcc slice descriptor will be returned on success and
+ * and error pointer is returned on failure
+ */
+struct llcc_slice_desc *llcc_slice_getd(struct device *dev, const char *name)
+{
+ struct device_node *np = dev->of_node;
+ int index = 0;
+ const char *slice_name;
+ struct property *prop;
+
+ if (!np) {
+ dev_err(dev, "%s() currently only supports DT\n", __func__);
+ return ERR_PTR(-ENOENT);
+ }
+
+ if (!of_get_property(np, "cache-slice-names", NULL)) {
+ dev_err(dev,
+ "%s() requires a \"cache-slice-names\" property\n",
+ __func__);
+ return ERR_PTR(-ENOENT);
+ }
+
+ of_property_for_each_string(np, "cache-slice-names", prop, slice_name) {
+ if (!strcmp(name, slice_name))
+ break;
+ index++;
+ }
+
+ return llcc_slice_get_entry(dev, index);
+}
+EXPORT_SYMBOL(llcc_slice_getd);
+
+/**
+ * llcc_slice_putd - llcc slice descritpor
+ * @desc: Pointer to llcc slice descriptor
+ */
+void llcc_slice_putd(struct llcc_slice_desc *desc)
+{
+ kfree(desc);
+}
+EXPORT_SYMBOL(llcc_slice_putd);
+
+static int llcc_update_act_ctrl(struct llcc_drv_data *drv, u32 sid,
+ u32 act_ctrl_reg_val, u32 status)
+{
+ u32 act_ctrl_reg;
+ u32 status_reg;
+ u32 slice_status;
+ unsigned long timeout;
+
+ act_ctrl_reg = drv->b_off + LLCC_TRP_ACT_CTRLn(sid);
+ status_reg = drv->b_off + LLCC_TRP_STATUSn(sid);
+
+ regmap_write(drv->llcc_map, act_ctrl_reg, act_ctrl_reg_val);
+
+ /* Make sure the activate trigger is applied before clearing it */
+ mb();
+
+ /* Clear the ACTIVE trigger */
+ act_ctrl_reg_val &= ~ACT_CTRL_ACT_TRIG;
+ regmap_write(drv->llcc_map, act_ctrl_reg, act_ctrl_reg_val);
+
+ timeout = jiffies + usecs_to_jiffies(LLCC_STATUS_READ_DELAY);
+ while (time_before(jiffies, timeout)) {
+ regmap_read(drv->llcc_map, status_reg, &slice_status);
+ if (!(slice_status & status))
+ return 0;
+ }
+
+ return -ETIMEDOUT;
+}
+
+/**
+ * llcc_slice_activate - Activate the llcc slice
+ * @desc: Pointer to llcc slice descriptor
+ *
+ * A value zero will be returned on success and a negative errno will
+ * be returned in error cases
+ */
+int llcc_slice_activate(struct llcc_slice_desc *desc)
+{
+ int rc = -EINVAL;
+ u32 act_ctrl_val;
+ struct llcc_drv_data *drv;
+
+ if (desc == NULL)
+ return rc;
+
+ drv = dev_get_drvdata(desc->dev);
+ if (!drv)
+ return rc;
+
+ mutex_lock(&drv->slice_mutex);
+ if (test_bit(desc->llcc_slice_id, drv->llcc_slice_map)) {
+ mutex_unlock(&drv->slice_mutex);
+ return 0;
+ }
+
+ act_ctrl_val = ACT_CTRL_OPCODE_ACTIVATE << ACT_CTRL_OPCODE_SHIFT;
+ act_ctrl_val |= ACT_CTRL_ACT_TRIG;
+
+ rc = llcc_update_act_ctrl(drv, desc->llcc_slice_id, act_ctrl_val,
+ DEACTIVATE);
+
+ __set_bit(desc->llcc_slice_id, drv->llcc_slice_map);
+ mutex_unlock(&drv->slice_mutex);
+
+ return rc;
+}
+EXPORT_SYMBOL(llcc_slice_activate);
+
+/**
+ * llcc_slice_deactivate - Deactivate the llcc slice
+ * @desc: Pointer to llcc slice descriptor
+ *
+ * A value zero will be returned on success and a negative errno will
+ * be returned in error cases
+ */
+int llcc_slice_deactivate(struct llcc_slice_desc *desc)
+{
+ u32 act_ctrl_val;
+ int rc = -EINVAL;
+ struct llcc_drv_data *drv;
+
+ if (desc == NULL)
+ return rc;
+
+ drv = dev_get_drvdata(desc->dev);
+ if (!drv)
+ return rc;
+
+ mutex_lock(&drv->slice_mutex);
+ if (!test_bit(desc->llcc_slice_id, drv->llcc_slice_map)) {
+ mutex_unlock(&drv->slice_mutex);
+ return 0;
+ }
+ act_ctrl_val = ACT_CTRL_OPCODE_DEACTIVATE << ACT_CTRL_OPCODE_SHIFT;
+ act_ctrl_val |= ACT_CTRL_ACT_TRIG;
+
+ rc = llcc_update_act_ctrl(drv, desc->llcc_slice_id, act_ctrl_val,
+ ACTIVATE);
+
+ __clear_bit(desc->llcc_slice_id, drv->llcc_slice_map);
+ mutex_unlock(&drv->slice_mutex);
+
+ return rc;
+}
+EXPORT_SYMBOL(llcc_slice_deactivate);
+
+/**
+ * llcc_get_slice_id - return the slice id
+ * @desc: Pointer to llcc slice descriptor
+ *
+ * A positive value will be returned on success and a negative errno will
+ * be returned on error
+ */
+int llcc_get_slice_id(struct llcc_slice_desc *desc)
+{
+ if (!desc)
+ return -EINVAL;
+
+ return desc->llcc_slice_id;
+}
+EXPORT_SYMBOL(llcc_get_slice_id);
+
+/**
+ * llcc_get_slice_size - return the slice id
+ * @desc: Pointer to llcc slice descriptor
+ *
+ * A positive value will be returned on success and zero will returned on
+ * error
+ */
+size_t llcc_get_slice_size(struct llcc_slice_desc *desc)
+{
+ if (!desc)
+ return 0;
+
+ return desc->llcc_slice_size;
+}
+EXPORT_SYMBOL(llcc_get_slice_size);
+
+static void qcom_llcc_cfg_program(struct platform_device *pdev)
+{
+ int i;
+ u32 attr1_cfg;
+ u32 attr0_cfg;
+ u32 attr1_val;
+ u32 attr0_val;
+ u32 max_cap_cacheline;
+ u32 sz;
+ const struct llcc_slice_config *llcc_table;
+ struct llcc_slice_desc desc;
+ struct llcc_drv_data *drv = platform_get_drvdata(pdev);
+ u32 b_off = drv->b_off;
+
+ sz = drv->llcc_config_data_sz;
+ llcc_table = drv->slice_data;
+
+ for (i = 0; i < sz; i++) {
+ attr1_cfg = b_off + LLCC_TRP_ATTR1_CFGn(llcc_table[i].slice_id);
+ attr0_cfg = b_off + LLCC_TRP_ATTR0_CFGn(llcc_table[i].slice_id);
+
+ attr1_val = llcc_table[i].cache_mode;
+ attr1_val |= (llcc_table[i].probe_target_ways <<
+ ATTR1_PROBE_TARGET_WAYS_SHIFT);
+ attr1_val |= (llcc_table[i].fixed_size <<
+ ATTR1_FIXED_SIZE_SHIFT);
+ attr1_val |= (llcc_table[i].priority << ATTR1_PRIORITY_SHIFT);
+
+ max_cap_cacheline = MAX_CAP_TO_BYTES(llcc_table[i].max_cap);
+
+ /* LLCC instances can vary for each target.
+ * The SW writes to broadcast register which gets propagated
+ * to each llcc instace (llcc0,.. llccN).
+ * Since the size of the memory is divided equally amongst the
+ * llcc instances, we need to configure the max cap accordingly.
+ */
+ max_cap_cacheline = (max_cap_cacheline / drv->num_banks);
+ max_cap_cacheline >>= CACHE_LINE_SIZE_SHIFT;
+ attr1_val |= (max_cap_cacheline << ATTR1_MAX_CAP_SHIFT);
+
+ attr0_val = llcc_table[i].res_ways & ATTR0_RES_WAYS_MASK;
+ attr0_val |= llcc_table[i].bonus_ways << ATR0_BONUS_WAYS_SHIFT;
+
+ regmap_write(drv->llcc_map, attr1_cfg, attr1_val);
+ regmap_write(drv->llcc_map, attr0_cfg, attr0_val);
+
+ /* Make sure that the SCT is programmed before activating */
+ mb();
+
+ if (llcc_table[i].activate_on_init) {
+ desc.llcc_slice_id = llcc_table[i].slice_id;
+ desc.dev = &pdev->dev;
+ if (llcc_slice_activate(&desc)) {
+ pr_err("activate slice id: %d timed out\n",
+ desc.llcc_slice_id);
+ }
+ }
+ }
+}
+
+int qcom_llcc_probe(struct platform_device *pdev,
+ const struct llcc_slice_config *llcc_cfg, u32 sz)
+{
+ int rc = 0;
+ u32 num_banks = 0;
+ struct device *dev = &pdev->dev;
+ static struct llcc_drv_data *drv_data;
+ struct resource *res;
+ void __iomem *base;
+
+ drv_data = devm_kzalloc(dev, sizeof(*drv_data), GFP_KERNEL);
+ if (!drv_data)
+ return PTR_ERR(drv_data);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base)) {
+ dev_err(&pdev->dev,"Cannot find the resource in device\n");
+ return PTR_ERR(base);
+ }
+
+ drv_data->llcc_map = devm_regmap_init_mmio(dev, base, &llcc_regmap_config);
+ if (IS_ERR(drv_data->llcc_map)) {
+ dev_err(&pdev->dev,"Cannot init the regmap for llcc\n");
+ return PTR_ERR(drv_data->llcc_map);
+ }
+
+ regmap_read(drv_data->llcc_map, LLCC_COMMON_STATUS0,
+ &num_banks);
+
+ num_banks &= LLCC_LB_CNT_MASK;
+ num_banks >>= LLCC_LB_CNT_SHIFT;
+ drv_data->num_banks = num_banks;
+
+ rc = of_property_read_u32(pdev->dev.of_node, "max-slices",
+ &drv_data->max_slices);
+ if (rc) {
+ dev_err(&pdev->dev, "Invalid max-slices dt entry\n");
+ devm_kfree(&pdev->dev, drv_data);
+ return rc;
+ }
+ drv_data->offsets = devm_kzalloc(dev, num_banks*sizeof(u32), GFP_KERNEL);
+ if (!drv_data->offsets) {
+ devm_kfree(&pdev->dev, drv_data);
+ return PTR_ERR(drv_data->offsets);
+ }
+
+ drv_data->offsets[0] = 0x0;
+ drv_data->offsets[1] = 0x80000;
+ drv_data->offsets[2] = 0x100000;
+ drv_data->offsets[3] = 0x180000;
+ drv_data->b_off = 0x200000;
+
+ drv_data->llcc_slice_map = kcalloc(BITS_TO_LONGS(drv_data->max_slices),
+ sizeof(unsigned long), GFP_KERNEL);
+
+ if (!drv_data->llcc_slice_map) {
+ devm_kfree(&pdev->dev, drv_data);
+ return PTR_ERR(drv_data->llcc_slice_map);
+ }
+
+ bitmap_zero(drv_data->llcc_slice_map, drv_data->max_slices);
+ drv_data->slice_data = llcc_cfg;
+ drv_data->llcc_config_data_sz = sz;
+ mutex_init(&drv_data->slice_mutex);
+ platform_set_drvdata(pdev, drv_data);
+
+ qcom_llcc_cfg_program(pdev);
+
+ return rc;
+}
+EXPORT_SYMBOL(qcom_llcc_probe);
+
+int qcom_llcc_remove(struct platform_device *pdev)
+{
+ static struct llcc_drv_data *drv_data;
+
+ drv_data = platform_get_drvdata(pdev);
+
+ mutex_destroy(&drv_data->slice_mutex);
+ kfree(drv_data->llcc_slice_map);
+ devm_kfree(&pdev->dev, drv_data);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+EXPORT_SYMBOL(qcom_llcc_remove);
diff --git a/include/linux/soc/qcom/llcc-qcom.h b/include/linux/soc/qcom/llcc-qcom.h
new file mode 100644
index 0000000..c408a8d
--- /dev/null
+++ b/include/linux/soc/qcom/llcc-qcom.h
@@ -0,0 +1,178 @@
+/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __LLCC_QCOM__
+#define __LLCC_QCOM__
+
+/**
+ * llcc_slice_desc - Cache slice descriptor
+ * @llcc_slice_id: llcc slice id
+ * @llcc_slice_size: Size allocated for the llcc slice
+ * @dev: pointer to llcc device
+ */
+struct llcc_slice_desc {
+ int llcc_slice_id;
+ size_t llcc_slice_size;
+ struct device *dev;
+};
+
+/**
+ * llcc_slice_config - Data associated with the llcc slice
+ * @name: name of the use case associated with the llcc slice
+ * @usecase_id: usecase id for which the llcc slice is used
+ * @slice_id: llcc slice id assigned to each slice
+ * @max_cap: maximum capacity of the llcc slice
+ * @priority: priority of the llcc slice
+ * @fixed_size: whether the llcc slice can grow beyond its size
+ * @bonus_ways: bonus ways associated with llcc slice
+ * @res_ways: reserved ways associated with llcc slice
+ * @cache_mode: mode of the llcce slice
+ * @probe_target_ways: Probe only reserved and bonus ways on a cache miss
+ * @dis_cap_alloc: Disable capacity based allocation
+ * @retain_on_pc: Retain through power collapse
+ * @activate_on_init: activate the slice on init
+ */
+struct llcc_slice_config {
+ const char *name;
+ int usecase_id;
+ int slice_id;
+ u32 max_cap;
+ u32 priority;
+ bool fixed_size;
+ u32 bonus_ways;
+ u32 res_ways;
+ u32 cache_mode;
+ u32 probe_target_ways;
+ bool dis_cap_alloc;
+ bool retain_on_pc;
+ u32 activate_on_init;
+};
+
+/**
+ * llcc_drv_data - Data associated with the llcc driver
+ * @llcc_map: regmap associated with the llcc device
+ * @slice_data: pointer to the data structure associated with slice
+ * @slice_mutex: mutex associated with each slice
+ * @llcc_config_data_sz: size of the config data table
+ * @max_slices: max slices as read from device tree
+ * @b_off: Offset of the broadcast bank
+ * @num_banks: Number of llcc banks
+ * @llcc_slice_map: Bit map to track the active slice ids
+ * @offsets: Pointer to the bank offsets array
+ */
+
+struct llcc_drv_data {
+ struct regmap *llcc_map;
+ const struct llcc_slice_config *slice_data;
+ struct mutex slice_mutex;
+ u32 llcc_config_data_sz;
+ u32 max_slices;
+ u32 b_off;
+ u32 num_banks;
+ unsigned long *llcc_slice_map;
+ u32 *offsets;
+};
+
+
+#ifdef CONFIG_QCOM_LLCC
+/**
+ * llcc_slice_getd - get llcc slice descriptor
+ * @dev: Device pointer of the client
+ * @name: Name of the use case
+ */
+struct llcc_slice_desc *llcc_slice_getd(struct device *dev, const char *name);
+
+/**
+ * llcc_slice_putd - llcc slice descritpor
+ * @desc: Pointer to llcc slice descriptor
+ */
+void llcc_slice_putd(struct llcc_slice_desc *desc);
+
+/**
+ * llcc_get_slice_id - get slice id
+ * @desc: Pointer to llcc slice descriptor
+ */
+int llcc_get_slice_id(struct llcc_slice_desc *desc);
+
+/**
+ * llcc_get_slice_size - llcc slice size
+ * @desc: Pointer to llcc slice descriptor
+ */
+size_t llcc_get_slice_size(struct llcc_slice_desc *desc);
+
+/**
+ * llcc_slice_activate - Activate the llcc slice
+ * @desc: Pointer to llcc slice descriptor
+ */
+int llcc_slice_activate(struct llcc_slice_desc *desc);
+
+/**
+ * llcc_slice_deactivate - Deactivate the llcc slice
+ * @desc: Pointer to llcc slice descriptor
+ */
+int llcc_slice_deactivate(struct llcc_slice_desc *desc);
+
+/**
+ * qcom_llcc_probe - program the sct table
+ * @pdev: platform device pointer
+ * @table: soc sct table
+ */
+int qcom_llcc_probe(struct platform_device *pdev,
+ const struct llcc_slice_config *table, u32 sz);
+/**
+ * qcom_llcc_remove - clean up llcc driver
+ * @pdev: platform driver pointer
+ */
+int qcom_llcc_remove(struct platform_device *pdev);
+#else
+static inline struct llcc_slice_desc *llcc_slice_getd(struct device *dev,
+ const char *name)
+{
+ return NULL;
+}
+
+static inline void llcc_slice_putd(struct llcc_slice_desc *desc)
+{
+
+};
+
+static inline int llcc_get_slice_id(struct llcc_slice_desc *desc)
+{
+ return -EINVAL;
+}
+
+static inline size_t llcc_get_slice_size(struct llcc_slice_desc *desc)
+{
+ return 0;
+}
+static inline int llcc_slice_activate(struct llcc_slice_desc *desc)
+{
+ return -EINVAL;
+}
+
+static inline int llcc_slice_deactivate(struct llcc_slice_desc *desc)
+{
+ return -EINVAL;
+}
+static inline int qcom_llcc_probe(struct platform_device *pdev,
+ const struct llcc_slice_config *table, u32 sz)
+{
+ return -ENODEV;
+}
+
+static inline int qcom_llcc_remove(struct platform_device *pdev)
+{
+ return -ENODEV;
+}
+#endif
+
+#endif
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
1) llcc-core.c file is missing, leading to failure in kernel compilation.
2) The Patch is also having some trailing whitespace errors.
On 3/25/2018 6:59 AM, Rishabh Bhatnagar wrote:
> LLCC (Last Level Cache Controller) provides additional cache memory
> in the system. LLCC is partitioned into muliple slices and each
> slice getting its own priority, size, ID and other config parameters.
> LLCC driver programs these parameters for each slice. Clients that
> are assigned to use LLCC need to get information such size & ID of the
> slice they get and activate or deactivate the slice as needed. LLCC driver
> provides API interfaces for the clients to perform these operations.
>
> Change-Id: I93c5f3225d0a917f0d6c64c7588a4e64e33a59ed
> Signed-off-by: Channagoud Kadabi <[email protected]>
> Signed-off-by: Rishabh Bhatnagar <[email protected]>
> ---
> drivers/soc/qcom/Kconfig | 16 ++
> drivers/soc/qcom/Makefile | 2 +
> drivers/soc/qcom/llcc-sdm845.c | 120 ++++++++++
> drivers/soc/qcom/llcc-slice.c | 454 +++++++++++++++++++++++++++++++++++++
> include/linux/soc/qcom/llcc-qcom.h | 178 +++++++++++++++
> 5 files changed, 770 insertions(+)
> create mode 100644 drivers/soc/qcom/llcc-sdm845.c
> create mode 100644 drivers/soc/qcom/llcc-slice.c
> create mode 100644 include/linux/soc/qcom/llcc-qcom.h
>
> diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
> index e050eb8..28237fc 100644
> --- a/drivers/soc/qcom/Kconfig
> +++ b/drivers/soc/qcom/Kconfig
> @@ -21,6 +21,22 @@ config QCOM_GSBI
> functions for connecting the underlying serial UART, SPI, and I2C
> devices to the output pins.
>
> +config QCOM_LLCC
> + tristate "Qualcomm Technologies, Inc. LLCC driver"
> + depends on ARCH_QCOM
> + help
> + Qualcomm Technologies, Inc. platform specific LLCC driver for Last
> + Level Cache. This provides interfaces to client's that use the LLCC.
> + Say yes here to enable LLCC slice driver.
> +
> +config QCOM_SDM845_LLCC
> + tristate "Qualcomm Technologies, Inc. SDM845 LLCC driver"
> + depends on QCOM_LLCC
> + help
> + Say yes here to enable the LLCC driver for SDM845. This is provides
> + data required to configure LLCC so that clients can start using the
> + LLCC slices.
> +
> config QCOM_MDT_LOADER
> tristate
> select QCOM_SCM
> diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
> index dcebf28..234560f 100644
> --- a/drivers/soc/qcom/Makefile
> +++ b/drivers/soc/qcom/Makefile
> @@ -12,3 +12,5 @@ obj-$(CONFIG_QCOM_SMEM_STATE) += smem_state.o
> obj-$(CONFIG_QCOM_SMP2P) += smp2p.o
> obj-$(CONFIG_QCOM_SMSM) += smsm.o
> obj-$(CONFIG_QCOM_WCNSS_CTRL) += wcnss_ctrl.o
> +obj-$(CONFIG_QCOM_LLCC) += llcc-core.o llcc-slice.o
> +obj-$(CONFIG_QCOM_SDM845_LLCC) += llcc-sdm845.o
> diff --git a/drivers/soc/qcom/llcc-sdm845.c b/drivers/soc/qcom/llcc-sdm845.c
> new file mode 100644
> index 0000000..cd431d9
> --- /dev/null
> +++ b/drivers/soc/qcom/llcc-sdm845.c
> @@ -0,0 +1,120 @@
> +/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 and
> + * only version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/err.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/soc/qcom/llcc-qcom.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/regmap.h>
> +
> +
> +/*
> + * SCT entry contains of the following parameters
> + * name: Name of the client's use case for which the llcc slice is used
> + * uid: Unique id for the client's use case
> + * slice_id: llcc slice id for each client
> + * max_cap: The maximum capacity of the cache slice provided in KB
> + * priority: Priority of the client used to select victim line for replacement
> + * fixed_size: Determine of the slice has a fixed capacity
> + * bonus_ways: Bonus ways to be used by any slice, bonus way is used only if
> + * it't not a reserved way.
> + * res_ways: Reserved ways for the cache slice, the reserved ways cannot be
> + * used by any other client than the one its assigned to.
> + * cache_mode: Each slice operates as a cache, this controls the mode of the
> + * slice normal or TCM
> + * probe_target_ways: Determines what ways to probe for access hit. When
> + * configured to 1 only bonus and reseved ways are probed.
> + * when configured to 0 all ways in llcc are probed.
> + * dis_cap_alloc: Disable capacity based allocation for a client
> + * retain_on_pc: If this bit is set and client has maitained active vote
> + * then the ways assigned to this client are not flushed on power
> + * collapse.
> + * activate_on_init: Activate the slice immidiately after the SCT is programmed
> + */
> +#define SCT_ENTRY(n, uid, sid, mc, p, fs, bway, rway, cmod, ptw, dca, rp, a) \
> + { \
> + .name = n, \
> + .usecase_id = uid, \
> + .slice_id = sid, \
> + .max_cap = mc, \
> + .priority = p, \
> + .fixed_size = fs, \
> + .bonus_ways = bway, \
> + .res_ways = rway, \
> + .cache_mode = cmod, \
> + .probe_target_ways = ptw, \
> + .dis_cap_alloc = dca, \
> + .retain_on_pc = rp, \
> + .activate_on_init = a, \
> + }
> +
> +
> +static struct llcc_slice_config sdm845_data[] = {
> + SCT_ENTRY("cpuss", 1, 1, 2816, 1, 0, 0xFFC, 0x2, 0, 0, 1, 1, 1),
> + SCT_ENTRY("vidsc0", 2, 2, 512, 2, 1, 0x0, 0x0F0, 0, 0, 1, 1, 0),
> + SCT_ENTRY("vidsc1", 3, 3, 512, 2, 1, 0x0, 0x0F0, 0, 0, 1, 1, 0),
> + SCT_ENTRY("rotator", 4, 4, 563, 2, 1, 0x0, 0x00e, 2, 0, 1, 1, 0),
> + SCT_ENTRY("voice", 5, 5, 2816, 1, 0, 0xFFC, 0x2, 0, 0, 1, 1, 0),
> + SCT_ENTRY("audio", 6, 6, 2816, 1, 0, 0xFFC, 0x2, 0, 0, 1, 1, 0),
> + SCT_ENTRY("modemhp_grow", 7, 7, 1024, 2, 0, 0x0FC, 0xF00, 0, 0, 1, 1, 0),
> + SCT_ENTRY("modem", 8, 8, 2816, 1, 0, 0xFFC, 0x2, 0, 0, 1, 1, 0),
> + SCT_ENTRY("compute", 10, 10, 2816, 1, 0, 0xFFC, 0x2, 0, 0, 1, 1, 0),
> + SCT_ENTRY("gpuhtw", 11, 11, 512, 1, 1, 0xC, 0x0, 0, 0, 1, 1, 0),
> + SCT_ENTRY("gpu", 12, 12, 2304, 1, 0, 0xFF0, 0x2, 0, 0, 1, 1, 0),
> + SCT_ENTRY("mmuhwt", 13, 13, 256, 2, 0, 0x0, 0x1, 0, 0, 1, 0, 1),
> + SCT_ENTRY("compute_dma", 15, 15, 2816, 1, 0, 0xFFC, 0x2, 0, 0, 1, 1, 0),
> + SCT_ENTRY("display", 16, 16, 2816, 1, 0, 0xFFC, 0x2, 0, 0, 1, 1, 0),
> + SCT_ENTRY("videofw", 17, 17, 2816, 1, 0, 0xFFC, 0x2, 0, 0, 1, 1, 0),
> + SCT_ENTRY("modemhp_fix", 20, 20, 1024, 2, 1, 0x0, 0xF00, 0, 0, 1, 1, 0),
> + SCT_ENTRY("modem_paging", 21, 21, 1024, 0, 1, 0x1e, 0x0, 0, 0, 1, 1, 0),
> + SCT_ENTRY("audiohw", 22, 22, 1024, 1, 1, 0xFFC, 0x2, 0, 0, 1, 1, 0),
> +};
> +
> +
> +static int sdm845_qcom_llcc_probe(struct platform_device *pdev)
> +{
> + return qcom_llcc_probe(pdev, sdm845_data,ARRAY_SIZE(sdm845_data));
> +}
> +
> +static const struct of_device_id sdm845_qcom_llcc_of_match[] = {
> + { .compatible = "qcom,sdm845-llcc", },
> + { },
> +};
> +
> +static struct platform_driver sdm845_qcom_llcc_driver = {
> + .driver = {
> + .name = "sdm845-llcc",
> + .owner = THIS_MODULE,
> + .of_match_table = sdm845_qcom_llcc_of_match,
> + },
> + .probe = sdm845_qcom_llcc_probe,
> + .remove = qcom_llcc_remove,
> +};
> +
> +static int __init sdm845_init_qcom_llcc_init(void)
> +{
> + return platform_driver_register(&sdm845_qcom_llcc_driver);
> +}
> +module_init(sdm845_init_qcom_llcc_init);
> +
> +static void __exit sdm845_exit_qcom_llcc_exit(void)
> +{
> + platform_driver_unregister(&sdm845_qcom_llcc_driver);
> +}
> +module_exit(sdm845_exit_qcom_llcc_exit);
> +
> +MODULE_DESCRIPTION("QTI sdm845 LLCC driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/soc/qcom/llcc-slice.c b/drivers/soc/qcom/llcc-slice.c
> new file mode 100644
> index 0000000..6e86770
> --- /dev/null
> +++ b/drivers/soc/qcom/llcc-slice.c
> @@ -0,0 +1,454 @@
> +/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 and
> + * only version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#define pr_fmt(fmt) "%s:" fmt, __func__
> +
> +#include <linux/kernel.h>
> +#include <linux/device.h>
> +#include <linux/delay.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/mutex.h>
> +#include <linux/bitmap.h>
> +#include <linux/bitops.h>
> +#include <linux/slab.h>
> +#include <linux/io.h>
> +#include <linux/err.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/regmap.h>
> +#include <linux/soc/qcom/llcc-qcom.h>
> +
> +#define ACTIVATE 0x1
> +#define DEACTIVATE 0x2
> +#define ACT_CTRL_OPCODE_ACTIVATE 0x1
> +#define ACT_CTRL_OPCODE_DEACTIVATE 0x2
> +#define ACT_CTRL_ACT_TRIG 0x1
> +#define ACT_CTRL_OPCODE_SHIFT 0x1
> +#define ATTR1_PROBE_TARGET_WAYS_SHIFT 0x2
> +#define ATTR1_FIXED_SIZE_SHIFT 0x3
> +#define ATTR1_PRIORITY_SHIFT 0x4
> +#define ATTR1_MAX_CAP_SHIFT 0x10
> +#define ATTR0_RES_WAYS_MASK 0x00000fff
> +#define ATR0_BONUS_WAYS_MASK 0x0fff0000
> +#define ATR0_BONUS_WAYS_SHIFT 0x10
> +#define LLCC_STATUS_READ_DELAY 100
> +
> +#define CACHE_LINE_SIZE_SHIFT 6
> +
> +#define LLCC_COMMON_STATUS0 0x0003000C
> +#define LLCC_LB_CNT_MASK 0xf0000000
> +#define LLCC_LB_CNT_SHIFT 28
> +
> +#define MAX_CAP_TO_BYTES(n) (n * 1024)
> +#define LLCC_TRP_ACT_CTRLn(n) (n * 0x1000)
> +#define LLCC_TRP_STATUSn(n) (4 + n * 0x1000)
> +#define LLCC_TRP_ATTR0_CFGn(n) (0x21000 + 0x8 * n)
> +#define LLCC_TRP_ATTR1_CFGn(n) (0x21004 + 0x8 * n)
> +
> +
> +
> +static const struct regmap_config llcc_regmap_config = {
> + .reg_bits = 32,
> + .reg_stride = 4,
> + .val_bits = 32,
> + .fast_io = true,
> +};
> +
> +
> +/* Get the slice entry by index */
> +static struct llcc_slice_desc *llcc_slice_get_entry(struct device *dev, int n)
> +{
> + struct of_phandle_args phargs;
> + struct llcc_drv_data *drv;
> + const struct llcc_slice_config *llcc_data_ptr;
> + struct llcc_slice_desc *desc;
> + struct platform_device *pdev;
> + u32 sz, count;
> +
> + if (of_parse_phandle_with_args(dev->of_node, "cache-slices",
> + "#cache-cells", n, &phargs)) {
> + pr_err("can't parse \"cache-slices\" property\n");
> + return ERR_PTR(-ENODEV);
> + }
> +
> + pdev = of_find_device_by_node(phargs.np);
> + if (!pdev) {
> + pr_err("Cannot find platform device from phandle\n");
> + return ERR_PTR(-ENODEV);
> + }
> +
> + drv = platform_get_drvdata(pdev);
> + if (!drv) {
> + pr_err("cannot find platform driver data\n");
> + return ERR_PTR(-EFAULT);
> + }
> +
> + llcc_data_ptr = drv->slice_data;
> + sz = drv->llcc_config_data_sz;
> + count = 0;
> +
> + while (llcc_data_ptr && count < sz) {
> + if (llcc_data_ptr->usecase_id == phargs.args[0])
> + break;
> + llcc_data_ptr++;
> + count++;
> + }
> +
> + if (llcc_data_ptr == NULL || count == sz) {
> + pr_err("can't find %d usecase id\n", phargs.args[0]);
> + return ERR_PTR(-ENODEV);
> + }
> +
> + desc = kzalloc(sizeof(struct llcc_slice_desc), GFP_KERNEL);
> + if (!desc)
> + return ERR_PTR(-ENOMEM);
> +
> + desc->llcc_slice_id = llcc_data_ptr->slice_id;
> + desc->llcc_slice_size = llcc_data_ptr->max_cap;
> + desc->dev = &pdev->dev;
> +
> + return desc;
> +}
> +
> +/**
> + * llcc_slice_getd - get llcc slice descriptor
> + * @dev: Device pointer of the client
> + * @name: Name of the use case
> + *
> + * A pointer to llcc slice descriptor will be returned on success and
> + * and error pointer is returned on failure
> + */
> +struct llcc_slice_desc *llcc_slice_getd(struct device *dev, const char *name)
> +{
> + struct device_node *np = dev->of_node;
> + int index = 0;
> + const char *slice_name;
> + struct property *prop;
> +
> + if (!np) {
> + dev_err(dev, "%s() currently only supports DT\n", __func__);
> + return ERR_PTR(-ENOENT);
> + }
> +
> + if (!of_get_property(np, "cache-slice-names", NULL)) {
> + dev_err(dev,
> + "%s() requires a \"cache-slice-names\" property\n",
> + __func__);
> + return ERR_PTR(-ENOENT);
> + }
> +
> + of_property_for_each_string(np, "cache-slice-names", prop, slice_name) {
> + if (!strcmp(name, slice_name))
> + break;
> + index++;
> + }
> +
> + return llcc_slice_get_entry(dev, index);
> +}
> +EXPORT_SYMBOL(llcc_slice_getd);
> +
> +/**
> + * llcc_slice_putd - llcc slice descritpor
> + * @desc: Pointer to llcc slice descriptor
> + */
> +void llcc_slice_putd(struct llcc_slice_desc *desc)
> +{
> + kfree(desc);
> +}
> +EXPORT_SYMBOL(llcc_slice_putd);
> +
> +static int llcc_update_act_ctrl(struct llcc_drv_data *drv, u32 sid,
> + u32 act_ctrl_reg_val, u32 status)
> +{
> + u32 act_ctrl_reg;
> + u32 status_reg;
> + u32 slice_status;
> + unsigned long timeout;
> +
> + act_ctrl_reg = drv->b_off + LLCC_TRP_ACT_CTRLn(sid);
> + status_reg = drv->b_off + LLCC_TRP_STATUSn(sid);
> +
> + regmap_write(drv->llcc_map, act_ctrl_reg, act_ctrl_reg_val);
> +
> + /* Make sure the activate trigger is applied before clearing it */
> + mb();
> +
> + /* Clear the ACTIVE trigger */
> + act_ctrl_reg_val &= ~ACT_CTRL_ACT_TRIG;
> + regmap_write(drv->llcc_map, act_ctrl_reg, act_ctrl_reg_val);
> +
> + timeout = jiffies + usecs_to_jiffies(LLCC_STATUS_READ_DELAY);
> + while (time_before(jiffies, timeout)) {
> + regmap_read(drv->llcc_map, status_reg, &slice_status);
> + if (!(slice_status & status))
> + return 0;
> + }
> +
> + return -ETIMEDOUT;
> +}
> +
> +/**
> + * llcc_slice_activate - Activate the llcc slice
> + * @desc: Pointer to llcc slice descriptor
> + *
> + * A value zero will be returned on success and a negative errno will
> + * be returned in error cases
> + */
> +int llcc_slice_activate(struct llcc_slice_desc *desc)
> +{
> + int rc = -EINVAL;
> + u32 act_ctrl_val;
> + struct llcc_drv_data *drv;
> +
> + if (desc == NULL)
> + return rc;
> +
> + drv = dev_get_drvdata(desc->dev);
> + if (!drv)
> + return rc;
> +
> + mutex_lock(&drv->slice_mutex);
> + if (test_bit(desc->llcc_slice_id, drv->llcc_slice_map)) {
> + mutex_unlock(&drv->slice_mutex);
> + return 0;
> + }
> +
> + act_ctrl_val = ACT_CTRL_OPCODE_ACTIVATE << ACT_CTRL_OPCODE_SHIFT;
> + act_ctrl_val |= ACT_CTRL_ACT_TRIG;
> +
> + rc = llcc_update_act_ctrl(drv, desc->llcc_slice_id, act_ctrl_val,
> + DEACTIVATE);
> +
> + __set_bit(desc->llcc_slice_id, drv->llcc_slice_map);
> + mutex_unlock(&drv->slice_mutex);
> +
> + return rc;
> +}
> +EXPORT_SYMBOL(llcc_slice_activate);
> +
> +/**
> + * llcc_slice_deactivate - Deactivate the llcc slice
> + * @desc: Pointer to llcc slice descriptor
> + *
> + * A value zero will be returned on success and a negative errno will
> + * be returned in error cases
> + */
> +int llcc_slice_deactivate(struct llcc_slice_desc *desc)
> +{
> + u32 act_ctrl_val;
> + int rc = -EINVAL;
> + struct llcc_drv_data *drv;
> +
> + if (desc == NULL)
> + return rc;
> +
> + drv = dev_get_drvdata(desc->dev);
> + if (!drv)
> + return rc;
> +
> + mutex_lock(&drv->slice_mutex);
> + if (!test_bit(desc->llcc_slice_id, drv->llcc_slice_map)) {
> + mutex_unlock(&drv->slice_mutex);
> + return 0;
> + }
> + act_ctrl_val = ACT_CTRL_OPCODE_DEACTIVATE << ACT_CTRL_OPCODE_SHIFT;
> + act_ctrl_val |= ACT_CTRL_ACT_TRIG;
> +
> + rc = llcc_update_act_ctrl(drv, desc->llcc_slice_id, act_ctrl_val,
> + ACTIVATE);
> +
> + __clear_bit(desc->llcc_slice_id, drv->llcc_slice_map);
> + mutex_unlock(&drv->slice_mutex);
> +
> + return rc;
> +}
> +EXPORT_SYMBOL(llcc_slice_deactivate);
> +
> +/**
> + * llcc_get_slice_id - return the slice id
> + * @desc: Pointer to llcc slice descriptor
> + *
> + * A positive value will be returned on success and a negative errno will
> + * be returned on error
> + */
> +int llcc_get_slice_id(struct llcc_slice_desc *desc)
> +{
> + if (!desc)
> + return -EINVAL;
> +
> + return desc->llcc_slice_id;
> +}
> +EXPORT_SYMBOL(llcc_get_slice_id);
> +
> +/**
> + * llcc_get_slice_size - return the slice id
> + * @desc: Pointer to llcc slice descriptor
> + *
> + * A positive value will be returned on success and zero will returned on
> + * error
> + */
> +size_t llcc_get_slice_size(struct llcc_slice_desc *desc)
> +{
> + if (!desc)
> + return 0;
> +
> + return desc->llcc_slice_size;
> +}
> +EXPORT_SYMBOL(llcc_get_slice_size);
> +
> +static void qcom_llcc_cfg_program(struct platform_device *pdev)
> +{
> + int i;
> + u32 attr1_cfg;
> + u32 attr0_cfg;
> + u32 attr1_val;
> + u32 attr0_val;
> + u32 max_cap_cacheline;
> + u32 sz;
> + const struct llcc_slice_config *llcc_table;
> + struct llcc_slice_desc desc;
> + struct llcc_drv_data *drv = platform_get_drvdata(pdev);
> + u32 b_off = drv->b_off;
> +
> + sz = drv->llcc_config_data_sz;
> + llcc_table = drv->slice_data;
> +
> + for (i = 0; i < sz; i++) {
> + attr1_cfg = b_off + LLCC_TRP_ATTR1_CFGn(llcc_table[i].slice_id);
> + attr0_cfg = b_off + LLCC_TRP_ATTR0_CFGn(llcc_table[i].slice_id);
> +
> + attr1_val = llcc_table[i].cache_mode;
> + attr1_val |= (llcc_table[i].probe_target_ways <<
> + ATTR1_PROBE_TARGET_WAYS_SHIFT);
> + attr1_val |= (llcc_table[i].fixed_size <<
> + ATTR1_FIXED_SIZE_SHIFT);
> + attr1_val |= (llcc_table[i].priority << ATTR1_PRIORITY_SHIFT);
> +
> + max_cap_cacheline = MAX_CAP_TO_BYTES(llcc_table[i].max_cap);
> +
> + /* LLCC instances can vary for each target.
> + * The SW writes to broadcast register which gets propagated
> + * to each llcc instace (llcc0,.. llccN).
> + * Since the size of the memory is divided equally amongst the
> + * llcc instances, we need to configure the max cap accordingly.
> + */
> + max_cap_cacheline = (max_cap_cacheline / drv->num_banks);
> + max_cap_cacheline >>= CACHE_LINE_SIZE_SHIFT;
> + attr1_val |= (max_cap_cacheline << ATTR1_MAX_CAP_SHIFT);
> +
> + attr0_val = llcc_table[i].res_ways & ATTR0_RES_WAYS_MASK;
> + attr0_val |= llcc_table[i].bonus_ways << ATR0_BONUS_WAYS_SHIFT;
> +
> + regmap_write(drv->llcc_map, attr1_cfg, attr1_val);
> + regmap_write(drv->llcc_map, attr0_cfg, attr0_val);
> +
> + /* Make sure that the SCT is programmed before activating */
> + mb();
> +
> + if (llcc_table[i].activate_on_init) {
> + desc.llcc_slice_id = llcc_table[i].slice_id;
> + desc.dev = &pdev->dev;
> + if (llcc_slice_activate(&desc)) {
> + pr_err("activate slice id: %d timed out\n",
> + desc.llcc_slice_id);
> + }
> + }
> + }
> +}
> +
> +int qcom_llcc_probe(struct platform_device *pdev,
> + const struct llcc_slice_config *llcc_cfg, u32 sz)
> +{
> + int rc = 0;
> + u32 num_banks = 0;
> + struct device *dev = &pdev->dev;
> + static struct llcc_drv_data *drv_data;
> + struct resource *res;
> + void __iomem *base;
> +
> + drv_data = devm_kzalloc(dev, sizeof(*drv_data), GFP_KERNEL);
> + if (!drv_data)
> + return PTR_ERR(drv_data);
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + base = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(base)) {
> + dev_err(&pdev->dev,"Cannot find the resource in device\n");
> + return PTR_ERR(base);
> + }
> +
> + drv_data->llcc_map = devm_regmap_init_mmio(dev, base, &llcc_regmap_config);
> + if (IS_ERR(drv_data->llcc_map)) {
> + dev_err(&pdev->dev,"Cannot init the regmap for llcc\n");
> + return PTR_ERR(drv_data->llcc_map);
> + }
> +
> + regmap_read(drv_data->llcc_map, LLCC_COMMON_STATUS0,
> + &num_banks);
> +
> + num_banks &= LLCC_LB_CNT_MASK;
> + num_banks >>= LLCC_LB_CNT_SHIFT;
> + drv_data->num_banks = num_banks;
> +
> + rc = of_property_read_u32(pdev->dev.of_node, "max-slices",
> + &drv_data->max_slices);
> + if (rc) {
> + dev_err(&pdev->dev, "Invalid max-slices dt entry\n");
> + devm_kfree(&pdev->dev, drv_data);
> + return rc;
> + }
> + drv_data->offsets = devm_kzalloc(dev, num_banks*sizeof(u32), GFP_KERNEL);
> + if (!drv_data->offsets) {
> + devm_kfree(&pdev->dev, drv_data);
> + return PTR_ERR(drv_data->offsets);
> + }
> +
> + drv_data->offsets[0] = 0x0;
> + drv_data->offsets[1] = 0x80000;
> + drv_data->offsets[2] = 0x100000;
> + drv_data->offsets[3] = 0x180000;
> + drv_data->b_off = 0x200000;
> +
> + drv_data->llcc_slice_map = kcalloc(BITS_TO_LONGS(drv_data->max_slices),
> + sizeof(unsigned long), GFP_KERNEL);
> +
> + if (!drv_data->llcc_slice_map) {
> + devm_kfree(&pdev->dev, drv_data);
> + return PTR_ERR(drv_data->llcc_slice_map);
> + }
> +
> + bitmap_zero(drv_data->llcc_slice_map, drv_data->max_slices);
> + drv_data->slice_data = llcc_cfg;
> + drv_data->llcc_config_data_sz = sz;
> + mutex_init(&drv_data->slice_mutex);
> + platform_set_drvdata(pdev, drv_data);
> +
> + qcom_llcc_cfg_program(pdev);
> +
> + return rc;
> +}
> +EXPORT_SYMBOL(qcom_llcc_probe);
> +
> +int qcom_llcc_remove(struct platform_device *pdev)
> +{
> + static struct llcc_drv_data *drv_data;
> +
> + drv_data = platform_get_drvdata(pdev);
> +
> + mutex_destroy(&drv_data->slice_mutex);
> + kfree(drv_data->llcc_slice_map);
> + devm_kfree(&pdev->dev, drv_data);
> + platform_set_drvdata(pdev, NULL);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL(qcom_llcc_remove);
> diff --git a/include/linux/soc/qcom/llcc-qcom.h b/include/linux/soc/qcom/llcc-qcom.h
> new file mode 100644
> index 0000000..c408a8d
> --- /dev/null
> +++ b/include/linux/soc/qcom/llcc-qcom.h
> @@ -0,0 +1,178 @@
> +/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 and
> + * only version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef __LLCC_QCOM__
> +#define __LLCC_QCOM__
> +
> +/**
> + * llcc_slice_desc - Cache slice descriptor
> + * @llcc_slice_id: llcc slice id
> + * @llcc_slice_size: Size allocated for the llcc slice
> + * @dev: pointer to llcc device
> + */
> +struct llcc_slice_desc {
> + int llcc_slice_id;
> + size_t llcc_slice_size;
> + struct device *dev;
> +};
> +
> +/**
> + * llcc_slice_config - Data associated with the llcc slice
> + * @name: name of the use case associated with the llcc slice
> + * @usecase_id: usecase id for which the llcc slice is used
> + * @slice_id: llcc slice id assigned to each slice
> + * @max_cap: maximum capacity of the llcc slice
> + * @priority: priority of the llcc slice
> + * @fixed_size: whether the llcc slice can grow beyond its size
> + * @bonus_ways: bonus ways associated with llcc slice
> + * @res_ways: reserved ways associated with llcc slice
> + * @cache_mode: mode of the llcce slice
> + * @probe_target_ways: Probe only reserved and bonus ways on a cache miss
> + * @dis_cap_alloc: Disable capacity based allocation
> + * @retain_on_pc: Retain through power collapse
> + * @activate_on_init: activate the slice on init
> + */
> +struct llcc_slice_config {
> + const char *name;
> + int usecase_id;
> + int slice_id;
> + u32 max_cap;
> + u32 priority;
> + bool fixed_size;
> + u32 bonus_ways;
> + u32 res_ways;
> + u32 cache_mode;
> + u32 probe_target_ways;
> + bool dis_cap_alloc;
> + bool retain_on_pc;
> + u32 activate_on_init;
> +};
> +
> +/**
> + * llcc_drv_data - Data associated with the llcc driver
> + * @llcc_map: regmap associated with the llcc device
> + * @slice_data: pointer to the data structure associated with slice
> + * @slice_mutex: mutex associated with each slice
> + * @llcc_config_data_sz: size of the config data table
> + * @max_slices: max slices as read from device tree
> + * @b_off: Offset of the broadcast bank
> + * @num_banks: Number of llcc banks
> + * @llcc_slice_map: Bit map to track the active slice ids
> + * @offsets: Pointer to the bank offsets array
> + */
> +
> +struct llcc_drv_data {
> + struct regmap *llcc_map;
> + const struct llcc_slice_config *slice_data;
> + struct mutex slice_mutex;
> + u32 llcc_config_data_sz;
> + u32 max_slices;
> + u32 b_off;
> + u32 num_banks;
> + unsigned long *llcc_slice_map;
> + u32 *offsets;
> +};
> +
> +
> +#ifdef CONFIG_QCOM_LLCC
> +/**
> + * llcc_slice_getd - get llcc slice descriptor
> + * @dev: Device pointer of the client
> + * @name: Name of the use case
> + */
> +struct llcc_slice_desc *llcc_slice_getd(struct device *dev, const char *name);
> +
> +/**
> + * llcc_slice_putd - llcc slice descritpor
> + * @desc: Pointer to llcc slice descriptor
> + */
> +void llcc_slice_putd(struct llcc_slice_desc *desc);
> +
> +/**
> + * llcc_get_slice_id - get slice id
> + * @desc: Pointer to llcc slice descriptor
> + */
> +int llcc_get_slice_id(struct llcc_slice_desc *desc);
> +
> +/**
> + * llcc_get_slice_size - llcc slice size
> + * @desc: Pointer to llcc slice descriptor
> + */
> +size_t llcc_get_slice_size(struct llcc_slice_desc *desc);
> +
> +/**
> + * llcc_slice_activate - Activate the llcc slice
> + * @desc: Pointer to llcc slice descriptor
> + */
> +int llcc_slice_activate(struct llcc_slice_desc *desc);
> +
> +/**
> + * llcc_slice_deactivate - Deactivate the llcc slice
> + * @desc: Pointer to llcc slice descriptor
> + */
> +int llcc_slice_deactivate(struct llcc_slice_desc *desc);
> +
> +/**
> + * qcom_llcc_probe - program the sct table
> + * @pdev: platform device pointer
> + * @table: soc sct table
> + */
> +int qcom_llcc_probe(struct platform_device *pdev,
> + const struct llcc_slice_config *table, u32 sz);
> +/**
> + * qcom_llcc_remove - clean up llcc driver
> + * @pdev: platform driver pointer
> + */
> +int qcom_llcc_remove(struct platform_device *pdev);
> +#else
> +static inline struct llcc_slice_desc *llcc_slice_getd(struct device *dev,
> + const char *name)
> +{
> + return NULL;
> +}
> +
> +static inline void llcc_slice_putd(struct llcc_slice_desc *desc)
> +{
> +
> +};
> +
> +static inline int llcc_get_slice_id(struct llcc_slice_desc *desc)
> +{
> + return -EINVAL;
> +}
> +
> +static inline size_t llcc_get_slice_size(struct llcc_slice_desc *desc)
> +{
> + return 0;
> +}
> +static inline int llcc_slice_activate(struct llcc_slice_desc *desc)
> +{
> + return -EINVAL;
> +}
> +
> +static inline int llcc_slice_deactivate(struct llcc_slice_desc *desc)
> +{
> + return -EINVAL;
> +}
> +static inline int qcom_llcc_probe(struct platform_device *pdev,
> + const struct llcc_slice_config *table, u32 sz)
> +{
> + return -ENODEV;
> +}
> +
> +static inline int qcom_llcc_remove(struct platform_device *pdev)
> +{
> + return -ENODEV;
> +}
> +#endif
> +
> +#endif
--
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project
Hi Rishabh,
Thank you for the patch! Yet something to improve:
[auto build test ERROR on linus/master]
[also build test ERROR on v4.16-rc7 next-20180327]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
url: https://github.com/0day-ci/linux/commits/Rishabh-Bhatnagar/SDM845-System-Cache-Driver/20180325-233519
config: arm-allmodconfig (attached as .config)
compiler: arm-linux-gnueabi-gcc (Debian 7.2.0-11) 7.2.0
reproduce:
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# save the attached .config to linux build tree
make.cross ARCH=arm
All errors (new ones prefixed by >>):
>> make[4]: *** No rule to make target 'drivers/soc/qcom/llcc-core.c', needed by 'drivers/soc/qcom/llcc-core.o'.
make[4]: *** [drivers/soc/qcom/llcc-slice.o] Error 1
make[4]: Target '__build' not remade because of errors.
--
>> drivers/soc/qcom/llcc-slice.c:130:25: error: redefinition of 'llcc_slice_getd'
struct llcc_slice_desc *llcc_slice_getd(struct device *dev, const char *name)
^~~~~~~~~~~~~~~
In file included from drivers/soc/qcom/llcc-slice.c:28:0:
include/linux/soc/qcom/llcc-qcom.h:137:39: note: previous definition of 'llcc_slice_getd' was here
static inline struct llcc_slice_desc *llcc_slice_getd(struct device *dev,
^~~~~~~~~~~~~~~
>> drivers/soc/qcom/llcc-slice.c:163:6: error: redefinition of 'llcc_slice_putd'
void llcc_slice_putd(struct llcc_slice_desc *desc)
^~~~~~~~~~~~~~~
In file included from drivers/soc/qcom/llcc-slice.c:28:0:
include/linux/soc/qcom/llcc-qcom.h:143:20: note: previous definition of 'llcc_slice_putd' was here
static inline void llcc_slice_putd(struct llcc_slice_desc *desc)
^~~~~~~~~~~~~~~
>> drivers/soc/qcom/llcc-slice.c:206:5: error: redefinition of 'llcc_slice_activate'
int llcc_slice_activate(struct llcc_slice_desc *desc)
^~~~~~~~~~~~~~~~~~~
In file included from drivers/soc/qcom/llcc-slice.c:28:0:
include/linux/soc/qcom/llcc-qcom.h:157:19: note: previous definition of 'llcc_slice_activate' was here
static inline int llcc_slice_activate(struct llcc_slice_desc *desc)
^~~~~~~~~~~~~~~~~~~
>> drivers/soc/qcom/llcc-slice.c:245:5: error: redefinition of 'llcc_slice_deactivate'
int llcc_slice_deactivate(struct llcc_slice_desc *desc)
^~~~~~~~~~~~~~~~~~~~~
In file included from drivers/soc/qcom/llcc-slice.c:28:0:
include/linux/soc/qcom/llcc-qcom.h:162:19: note: previous definition of 'llcc_slice_deactivate' was here
static inline int llcc_slice_deactivate(struct llcc_slice_desc *desc)
^~~~~~~~~~~~~~~~~~~~~
>> drivers/soc/qcom/llcc-slice.c:283:5: error: redefinition of 'llcc_get_slice_id'
int llcc_get_slice_id(struct llcc_slice_desc *desc)
^~~~~~~~~~~~~~~~~
In file included from drivers/soc/qcom/llcc-slice.c:28:0:
include/linux/soc/qcom/llcc-qcom.h:148:19: note: previous definition of 'llcc_get_slice_id' was here
static inline int llcc_get_slice_id(struct llcc_slice_desc *desc)
^~~~~~~~~~~~~~~~~
>> drivers/soc/qcom/llcc-slice.c:299:8: error: redefinition of 'llcc_get_slice_size'
size_t llcc_get_slice_size(struct llcc_slice_desc *desc)
^~~~~~~~~~~~~~~~~~~
In file included from drivers/soc/qcom/llcc-slice.c:28:0:
include/linux/soc/qcom/llcc-qcom.h:153:22: note: previous definition of 'llcc_get_slice_size' was here
static inline size_t llcc_get_slice_size(struct llcc_slice_desc *desc)
^~~~~~~~~~~~~~~~~~~
>> drivers/soc/qcom/llcc-slice.c:368:5: error: redefinition of 'qcom_llcc_probe'
int qcom_llcc_probe(struct platform_device *pdev,
^~~~~~~~~~~~~~~
In file included from drivers/soc/qcom/llcc-slice.c:28:0:
include/linux/soc/qcom/llcc-qcom.h:166:19: note: previous definition of 'qcom_llcc_probe' was here
static inline int qcom_llcc_probe(struct platform_device *pdev,
^~~~~~~~~~~~~~~
>> drivers/soc/qcom/llcc-slice.c:441:5: error: redefinition of 'qcom_llcc_remove'
int qcom_llcc_remove(struct platform_device *pdev)
^~~~~~~~~~~~~~~~
In file included from drivers/soc/qcom/llcc-slice.c:28:0:
include/linux/soc/qcom/llcc-qcom.h:172:19: note: previous definition of 'qcom_llcc_remove' was here
static inline int qcom_llcc_remove(struct platform_device *pdev)
^~~~~~~~~~~~~~~~
vim +/llcc_slice_getd +130 drivers/soc/qcom/llcc-slice.c
121
122 /**
123 * llcc_slice_getd - get llcc slice descriptor
124 * @dev: Device pointer of the client
125 * @name: Name of the use case
126 *
127 * A pointer to llcc slice descriptor will be returned on success and
128 * and error pointer is returned on failure
129 */
> 130 struct llcc_slice_desc *llcc_slice_getd(struct device *dev, const char *name)
131 {
132 struct device_node *np = dev->of_node;
133 int index = 0;
134 const char *slice_name;
135 struct property *prop;
136
137 if (!np) {
138 dev_err(dev, "%s() currently only supports DT\n", __func__);
139 return ERR_PTR(-ENOENT);
140 }
141
142 if (!of_get_property(np, "cache-slice-names", NULL)) {
143 dev_err(dev,
144 "%s() requires a \"cache-slice-names\" property\n",
145 __func__);
146 return ERR_PTR(-ENOENT);
147 }
148
149 of_property_for_each_string(np, "cache-slice-names", prop, slice_name) {
150 if (!strcmp(name, slice_name))
151 break;
152 index++;
153 }
154
155 return llcc_slice_get_entry(dev, index);
156 }
157 EXPORT_SYMBOL(llcc_slice_getd);
158
159 /**
160 * llcc_slice_putd - llcc slice descritpor
161 * @desc: Pointer to llcc slice descriptor
162 */
> 163 void llcc_slice_putd(struct llcc_slice_desc *desc)
164 {
165 kfree(desc);
166 }
167 EXPORT_SYMBOL(llcc_slice_putd);
168
169 static int llcc_update_act_ctrl(struct llcc_drv_data *drv, u32 sid,
170 u32 act_ctrl_reg_val, u32 status)
171 {
172 u32 act_ctrl_reg;
173 u32 status_reg;
174 u32 slice_status;
175 unsigned long timeout;
176
177 act_ctrl_reg = drv->b_off + LLCC_TRP_ACT_CTRLn(sid);
178 status_reg = drv->b_off + LLCC_TRP_STATUSn(sid);
179
180 regmap_write(drv->llcc_map, act_ctrl_reg, act_ctrl_reg_val);
181
182 /* Make sure the activate trigger is applied before clearing it */
183 mb();
184
185 /* Clear the ACTIVE trigger */
186 act_ctrl_reg_val &= ~ACT_CTRL_ACT_TRIG;
187 regmap_write(drv->llcc_map, act_ctrl_reg, act_ctrl_reg_val);
188
189 timeout = jiffies + usecs_to_jiffies(LLCC_STATUS_READ_DELAY);
190 while (time_before(jiffies, timeout)) {
191 regmap_read(drv->llcc_map, status_reg, &slice_status);
192 if (!(slice_status & status))
193 return 0;
194 }
195
196 return -ETIMEDOUT;
197 }
198
199 /**
200 * llcc_slice_activate - Activate the llcc slice
201 * @desc: Pointer to llcc slice descriptor
202 *
203 * A value zero will be returned on success and a negative errno will
204 * be returned in error cases
205 */
> 206 int llcc_slice_activate(struct llcc_slice_desc *desc)
207 {
208 int rc = -EINVAL;
209 u32 act_ctrl_val;
210 struct llcc_drv_data *drv;
211
212 if (desc == NULL)
213 return rc;
214
215 drv = dev_get_drvdata(desc->dev);
216 if (!drv)
217 return rc;
218
219 mutex_lock(&drv->slice_mutex);
220 if (test_bit(desc->llcc_slice_id, drv->llcc_slice_map)) {
221 mutex_unlock(&drv->slice_mutex);
222 return 0;
223 }
224
225 act_ctrl_val = ACT_CTRL_OPCODE_ACTIVATE << ACT_CTRL_OPCODE_SHIFT;
226 act_ctrl_val |= ACT_CTRL_ACT_TRIG;
227
228 rc = llcc_update_act_ctrl(drv, desc->llcc_slice_id, act_ctrl_val,
229 DEACTIVATE);
230
231 __set_bit(desc->llcc_slice_id, drv->llcc_slice_map);
232 mutex_unlock(&drv->slice_mutex);
233
234 return rc;
235 }
236 EXPORT_SYMBOL(llcc_slice_activate);
237
238 /**
239 * llcc_slice_deactivate - Deactivate the llcc slice
240 * @desc: Pointer to llcc slice descriptor
241 *
242 * A value zero will be returned on success and a negative errno will
243 * be returned in error cases
244 */
> 245 int llcc_slice_deactivate(struct llcc_slice_desc *desc)
246 {
247 u32 act_ctrl_val;
248 int rc = -EINVAL;
249 struct llcc_drv_data *drv;
250
251 if (desc == NULL)
252 return rc;
253
254 drv = dev_get_drvdata(desc->dev);
255 if (!drv)
256 return rc;
257
258 mutex_lock(&drv->slice_mutex);
259 if (!test_bit(desc->llcc_slice_id, drv->llcc_slice_map)) {
260 mutex_unlock(&drv->slice_mutex);
261 return 0;
262 }
263 act_ctrl_val = ACT_CTRL_OPCODE_DEACTIVATE << ACT_CTRL_OPCODE_SHIFT;
264 act_ctrl_val |= ACT_CTRL_ACT_TRIG;
265
266 rc = llcc_update_act_ctrl(drv, desc->llcc_slice_id, act_ctrl_val,
267 ACTIVATE);
268
269 __clear_bit(desc->llcc_slice_id, drv->llcc_slice_map);
270 mutex_unlock(&drv->slice_mutex);
271
272 return rc;
273 }
274 EXPORT_SYMBOL(llcc_slice_deactivate);
275
276 /**
277 * llcc_get_slice_id - return the slice id
278 * @desc: Pointer to llcc slice descriptor
279 *
280 * A positive value will be returned on success and a negative errno will
281 * be returned on error
282 */
> 283 int llcc_get_slice_id(struct llcc_slice_desc *desc)
284 {
285 if (!desc)
286 return -EINVAL;
287
288 return desc->llcc_slice_id;
289 }
290 EXPORT_SYMBOL(llcc_get_slice_id);
291
292 /**
293 * llcc_get_slice_size - return the slice id
294 * @desc: Pointer to llcc slice descriptor
295 *
296 * A positive value will be returned on success and zero will returned on
297 * error
298 */
> 299 size_t llcc_get_slice_size(struct llcc_slice_desc *desc)
300 {
301 if (!desc)
302 return 0;
303
304 return desc->llcc_slice_size;
305 }
306 EXPORT_SYMBOL(llcc_get_slice_size);
307
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation