diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 0cc6fa88a236..ac2bfa2f59a6 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -43,7 +43,8 @@ obj-$(CONFIG_QCOM_IPCC) += qcom_ipcc.o obj-$(CONFIG_QCOM_RIMPS) += qcom_rimps.o obj-$(CONFIG_QCOM_RUN_QUEUE_STATS) += rq_stats.o obj-$(CONFIG_QCOM_APR) += apr.o -obj-$(CONFIG_QCOM_LLCC) += llcc-slice.o +obj-$(CONFIG_QCOM_LLCC) += qcom_llcc.o +qcom_llcc-y += llcc-slice.o llcc-tcm.o obj-$(CONFIG_QCOM_LAHAINA_LLCC) += llcc-lahaina.o obj-$(CONFIG_QCOM_SDXLEMUR_LLCC) += llcc-sdxlemur.o obj-$(CONFIG_QCOM_SHIMA_LLCC) += llcc-shima.o diff --git a/drivers/soc/qcom/llcc-slice.c b/drivers/soc/qcom/llcc-slice.c index 459f2de9b25d..eb09736b924e 100644 --- a/drivers/soc/qcom/llcc-slice.c +++ b/drivers/soc/qcom/llcc-slice.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved. * */ @@ -16,6 +16,7 @@ #include #include #include +#include #define ACTIVATE BIT(0) #define DEACTIVATE BIT(1) @@ -377,6 +378,7 @@ int qcom_llcc_probe(struct platform_device *pdev, struct device *dev = &pdev->dev; int ret, i; struct platform_device *llcc_edac, *llcc_perfmon; + struct device_node *tcm_memory_node; drv_data = devm_kzalloc(dev, sizeof(*drv_data), GFP_KERNEL); if (!drv_data) { @@ -459,7 +461,20 @@ int qcom_llcc_probe(struct platform_device *pdev, if (IS_ERR(llcc_perfmon)) dev_err(dev, "Failed to register llcc perfmon device\n"); + tcm_memory_node = of_parse_phandle(dev->of_node, "memory-region", 0); + if (tcm_memory_node) { + ret = qcom_llcc_tcm_probe(pdev, llcc_cfg, sz, tcm_memory_node); + if (ret) { + dev_err(dev, "Failed to probe TCM manager\n"); + goto err_dereg; + } + } + return 0; + +err_dereg: + platform_device_unregister(llcc_edac); + platform_device_unregister(llcc_perfmon); err: drv_data = ERR_PTR(-ENODEV); return ret; diff --git a/drivers/soc/qcom/llcc-tcm.c b/drivers/soc/qcom/llcc-tcm.c new file mode 100644 index 000000000000..abfc9720dc39 --- /dev/null +++ b/drivers/soc/qcom/llcc-tcm.c @@ -0,0 +1,214 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2021, The Linux Foundation. All rights reserved. + */ +#include +#include +#include +#include +#include + +#include +#include + +struct llcc_tcm_drv_data { + struct device *dev; + struct llcc_slice_desc *tcm_slice; + struct llcc_tcm_data *tcm_data; + bool is_active; + bool activate_on_init; + struct mutex lock; +}; + +static struct llcc_tcm_drv_data *drv_data = (void *) -EPROBE_DEFER; + +/** + * qcom_llcc_tcm_probe - Probes the tcm manager + * @pdev: the platform device for the llcc driver + * @table: the llcc slice table + * @size: the size of the llcc slice table + * @node: the memory-regions node in the llcc device tree entry + * + * Returns 0 on success and a negative error code on failure + */ +int qcom_llcc_tcm_probe(struct platform_device *pdev, + const struct llcc_slice_config *table, size_t size, + struct device_node *node) +{ + u32 i; + int ret; + struct resource r; + + drv_data = devm_kzalloc(&pdev->dev, sizeof(struct llcc_tcm_drv_data), + GFP_KERNEL); + + if (!drv_data) { + pr_err("Failed to allocate tcm driver data\n"); + ret = -ENOMEM; + goto cfg_err; + } + + drv_data->tcm_data = devm_kzalloc(&pdev->dev, + sizeof(struct llcc_tcm_data), GFP_KERNEL); + + if (!drv_data->tcm_data) { + pr_err("Failed to allocate tcm user data\n"); + ret = -ENOMEM; + goto cfg_err; + } + + drv_data->dev = &pdev->dev; + drv_data->tcm_slice = llcc_slice_getd(LLCC_APTCM); + if (IS_ERR_OR_NULL(drv_data->tcm_slice)) { + pr_err("Failed to get tcm slice from llcc driver\n"); + ret = -ENODEV; + goto cfg_err; + } + + for (i = 0; i < size; i++) { + if (table[i].usecase_id == LLCC_APTCM) { + drv_data->activate_on_init = table[i].activate_on_init; + break; + } + } + + ret = of_address_to_resource(node, 0, &r); + if (ret) + goto slice_cfg_err; + of_node_put(node); + + drv_data->tcm_data->phys_addr = r.start; + drv_data->tcm_data->mem_size = + drv_data->tcm_slice->slice_size * SZ_1K; + drv_data->tcm_data->virt_addr = ioremap(drv_data->tcm_data->phys_addr, + drv_data->tcm_data->mem_size); + if (IS_ERR_OR_NULL(drv_data->tcm_data->virt_addr)) + goto slice_cfg_err; + + + mutex_init(&drv_data->lock); + + return 0; + +slice_cfg_err: + llcc_slice_putd(drv_data->tcm_slice); +cfg_err: + drv_data = ERR_PTR(-ENODEV); + return ret; +} +EXPORT_SYMBOL(qcom_llcc_tcm_probe); + +/** + * llcc_tcm_activate - Activate the TCM slice and give exclusive access + * + * A valid pointer to a struct llcc_tcm_data will be returned on success + * and error pointer on failure + */ +struct llcc_tcm_data *llcc_tcm_activate(void) +{ + int ret; + + if (IS_ERR(drv_data)) + return ERR_PTR(-EPROBE_DEFER); + + mutex_lock(&drv_data->lock); + if (IS_ERR_OR_NULL(drv_data->tcm_slice) || + IS_ERR_OR_NULL(drv_data->tcm_data) || + drv_data->is_active) { + ret = -EBUSY; + goto act_err; + } + + /* Should go through anyways if slice is already activated, */ + /* but if not already activated through the TCM manager */ + ret = llcc_slice_activate(drv_data->tcm_slice); + if (ret) { + if (drv_data->activate_on_init) + goto act_err; + else + goto act_err_deact; + } + + drv_data->is_active = true; + + mutex_unlock(&drv_data->lock); + return drv_data->tcm_data; + +act_err_deact: + llcc_slice_deactivate(drv_data->tcm_slice); +act_err: + mutex_unlock(&drv_data->lock); + return ERR_PTR(ret); +} +EXPORT_SYMBOL(llcc_tcm_activate); + +/** + * llcc_tcm_deactivate - Deactivate the TCM slice and revoke exclusive access + * @tcm_data: Pointer to the tcm data descriptor + */ +void llcc_tcm_deactivate(struct llcc_tcm_data *tcm_data) +{ + if (IS_ERR(drv_data) || IS_ERR_OR_NULL(tcm_data)) + return; + + mutex_lock(&drv_data->lock); + if (IS_ERR_OR_NULL(drv_data->tcm_slice) || + IS_ERR_OR_NULL(drv_data->tcm_data) || + !drv_data->is_active) { + mutex_unlock(&drv_data->lock); + return; + } + + if (!drv_data->activate_on_init) + llcc_slice_deactivate(drv_data->tcm_slice); + + drv_data->is_active = false; + + mutex_unlock(&drv_data->lock); +} +EXPORT_SYMBOL(llcc_tcm_deactivate); + +/** + * llcc_tcm_get_phys_addr - Gets the physical address of the tcm slice + * @tcm_data: Pointer to the tcm data descriptor + * + * Returns the physical address on success and 0 on failure + */ +phys_addr_t llcc_tcm_get_phys_addr(struct llcc_tcm_data *tcm_data) +{ + if (IS_ERR_OR_NULL(tcm_data)) + return 0; + + return tcm_data->phys_addr; +} +EXPORT_SYMBOL(llcc_tcm_get_phys_addr); + +/** + * llcc_tcm_get_virt_addr - Gets the virtual address of the tcm slice + * @tcm_data: Pointer to the tcm data descriptor + * + * Returns the virtual address on success and NULL on failure + */ +void __iomem *llcc_tcm_get_virt_addr(struct llcc_tcm_data *tcm_data) +{ + if (IS_ERR_OR_NULL(tcm_data)) + return NULL; + + return tcm_data->virt_addr; +} +EXPORT_SYMBOL(llcc_tcm_get_virt_addr); + +/** + * llcc_tcm_get_slice_size - Gets the size of the tcm slice + * @tcm_data: Pointer to the tcm data descriptor + * + * Returns the size of the slice on success and 0 on failure + */ +size_t llcc_tcm_get_slice_size(struct llcc_tcm_data *tcm_data) +{ + if (IS_ERR_OR_NULL(tcm_data)) + return 0; + + return tcm_data->mem_size; +} +EXPORT_SYMBOL(llcc_tcm_get_slice_size); diff --git a/include/linux/soc/qcom/llcc-tcm.h b/include/linux/soc/qcom/llcc-tcm.h new file mode 100644 index 000000000000..1ce35ef80d73 --- /dev/null +++ b/include/linux/soc/qcom/llcc-tcm.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2021, The Linux Foundation. All rights reserved. + */ + +#ifndef _TCM_QCOM_H_ +#define _TCM_QCOM_H_ + +struct llcc_tcm_data { + phys_addr_t phys_addr; + void __iomem *virt_addr; + size_t mem_size; +}; + +int qcom_llcc_tcm_probe(struct platform_device *pdev, + const struct llcc_slice_config *table, size_t size, + struct device_node *node); + +struct llcc_tcm_data *llcc_tcm_activate(void); + +phys_addr_t llcc_tcm_get_phys_addr(struct llcc_tcm_data *tcm_data); + +void __iomem *llcc_tcm_get_virt_addr(struct llcc_tcm_data *tcm_data); + +size_t llcc_tcm_get_slice_size(struct llcc_tcm_data *tcm_data); + +void llcc_tcm_deactivate(struct llcc_tcm_data *tcm_data); + +#endif //_TCM_QCOM_H_