123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
- */
- #include <linux/module.h>
- #include <linux/of_platform.h>
- #include <linux/platform_device.h>
- #include <linux/slab.h>
- #include <linux/soc/qcom/msm_mmrm.h>
- #include <linux/fs.h>
- #include "mmrm_internal.h"
- #include "mmrm_debug.h"
- #include "mmrm_clk_rsrc_mgr.h"
- #define VERIFY_PDEV(pdev) \
- { \
- if (!pdev) { \
- d_mpr_e("%s: null platform dev\n", __func__);\
- rc = -EINVAL; \
- goto err_exit; \
- } \
- }
- #define RESET_DRV_DATA(drv_data) \
- { \
- kfree(drv_data); \
- drv_data = (void *) -EPROBE_DEFER; \
- }
- #define CHECK_SKIP_MMRM_CLK_RSRC(drv_data) \
- { \
- if (!drv_data->is_clk_scaling_supported) { \
- d_mpr_h("%s: mmrm clk rsrc not supported\n", __func__);\
- goto skip_mmrm; \
- } \
- }
- #define MMRM_SYSFS_ENTRY_MAX_LEN PAGE_SIZE
- extern int msm_mmrm_debug;
- extern u8 msm_mmrm_enable_throttle_feature;
- extern u8 msm_mmrm_allow_multiple_register;
- struct mmrm_driver_data *drv_data = (void *) -EPROBE_DEFER;
- bool mmrm_client_check_scaling_supported(enum mmrm_client_type client_type, u32 client_domain)
- {
- if (drv_data == (void *)-EPROBE_DEFER) {
- d_mpr_e("%s: mmrm probe_init not done\n", __func__);
- goto err_exit;
- }
- if (client_type == MMRM_CLIENT_CLOCK) {
- CHECK_SKIP_MMRM_CLK_RSRC(drv_data);
- /* TODO: Check for individual domain */
- }
- return true;
- err_exit:
- d_mpr_e("%s: error exit\n", __func__);
- skip_mmrm:
- return false;
- }
- EXPORT_SYMBOL(mmrm_client_check_scaling_supported);
- struct mmrm_client *mmrm_client_register(struct mmrm_client_desc *client_desc)
- {
- struct mmrm_client *client = NULL;
- /* check for null input */
- if (!client_desc) {
- d_mpr_e("%s: null input descriptor\n", __func__);
- goto err_exit;
- }
- if (drv_data == (void *) -EPROBE_DEFER) {
- d_mpr_e("%s: mmrm probe_init not done\n", __func__);
- goto err_exit;
- }
- /* check for client type, then register */
- if (client_desc->client_type == MMRM_CLIENT_CLOCK) {
- /* check for skip mmrm */
- CHECK_SKIP_MMRM_CLK_RSRC(drv_data);
- client = mmrm_clk_client_register(
- drv_data->clk_mgr, client_desc);
- if (!client) {
- d_mpr_e("%s: failed to register client\n", __func__);
- goto err_exit;
- }
- } else {
- d_mpr_e("%s: unknown client_type %d\n",
- __func__, client_desc->client_type);
- goto err_exit;
- }
- skip_mmrm:
- return client;
- err_exit:
- d_mpr_e("%s: error exit\n", __func__);
- return client;
- }
- EXPORT_SYMBOL(mmrm_client_register);
- int mmrm_client_deregister(struct mmrm_client *client)
- {
- int rc = 0;
- /* check for null input */
- if (!client) {
- d_mpr_e("%s: invalid input client\n", __func__);
- rc = -EINVAL;
- goto err_exit;
- }
- if (drv_data == (void *) -EPROBE_DEFER) {
- d_mpr_e("%s: mmrm probe_init not done\n", __func__);
- goto err_exit;
- }
- /* check for client type, then deregister */
- if (client->client_type == MMRM_CLIENT_CLOCK) {
- /* check for skip mmrm */
- CHECK_SKIP_MMRM_CLK_RSRC(drv_data);
- rc = mmrm_clk_client_deregister(drv_data->clk_mgr, client);
- if (rc != 0) {
- d_mpr_e("%s: failed to deregister client\n", __func__);
- goto err_exit;
- }
- } else {
- d_mpr_e("%s: unknown client_type %d\n",
- __func__, client->client_type);
- }
- skip_mmrm:
- return rc;
- err_exit:
- d_mpr_e("%s: error = %d\n", __func__, rc);
- return rc;
- }
- EXPORT_SYMBOL(mmrm_client_deregister);
- int mmrm_client_set_value(struct mmrm_client *client,
- struct mmrm_client_data *client_data, unsigned long val)
- {
- int rc = 0;
- /* check for null input */
- if (!client || !client_data) {
- d_mpr_e("%s: invalid input client(%pK) client_data(%pK)\n",
- __func__, client, client_data);
- rc = -EINVAL;
- goto err_exit;
- }
- if (drv_data == (void *) -EPROBE_DEFER) {
- d_mpr_e("%s: mmrm probe_init not done\n", __func__);
- goto err_exit;
- }
- /* check for client type, then set value */
- if (client->client_type == MMRM_CLIENT_CLOCK) {
- /* check for skip mmrm */
- CHECK_SKIP_MMRM_CLK_RSRC(drv_data);
- rc = mmrm_clk_client_setval(drv_data->clk_mgr, client,
- client_data, val);
- if (rc != 0) {
- d_mpr_e("%s: failed to set value for client\n", __func__);
- goto err_exit;
- }
- } else {
- d_mpr_e("%s: unknown client_type %d\n",
- __func__, client->client_type);
- }
- skip_mmrm:
- return rc;
- err_exit:
- d_mpr_e("%s: error = %d\n", __func__, rc);
- return rc;
- }
- EXPORT_SYMBOL(mmrm_client_set_value);
- int mmrm_client_set_value_in_range(struct mmrm_client *client,
- struct mmrm_client_data *client_data,
- struct mmrm_client_res_value *val)
- {
- int rc = 0;
- /* check for null input */
- if (!client || !client_data || !val) {
- d_mpr_e(
- "%s: invalid input client(%pK) client_data(%pK) val(%pK)\n",
- __func__, client, client_data, val);
- rc = -EINVAL;
- goto err_exit;
- }
- if (drv_data == (void *) -EPROBE_DEFER) {
- d_mpr_e("%s: mmrm probe_init not done\n", __func__);
- goto err_exit;
- }
- /* check for client type, then set value */
- if (client->client_type == MMRM_CLIENT_CLOCK) {
- /* check for skip mmrm */
- CHECK_SKIP_MMRM_CLK_RSRC(drv_data);
- rc = mmrm_clk_client_setval_inrange(drv_data->clk_mgr,
- client, client_data, val);
- if (rc != 0) {
- d_mpr_e("%s: failed to set value for client\n", __func__);
- goto err_exit;
- }
- } else {
- d_mpr_e("%s: unknown client_type %d\n",
- __func__, client->client_type);
- }
- skip_mmrm:
- return rc;
- err_exit:
- d_mpr_e("%s: error = %d\n", __func__, rc);
- return rc;
- }
- EXPORT_SYMBOL(mmrm_client_set_value_in_range);
- int mmrm_client_get_value(struct mmrm_client *client,
- struct mmrm_client_res_value *val)
- {
- int rc = 0;
- /* check for null input */
- if (!client || !val) {
- d_mpr_e("%s: invalid input client(%pK) val(%pK)\n",
- __func__, client, val);
- rc = -EINVAL;
- goto err_exit;
- }
- if (drv_data == (void *) -EPROBE_DEFER) {
- d_mpr_e("%s: mmrm probe_init not done\n", __func__);
- goto err_exit;
- }
- /* check for client type, then get value */
- if (client->client_type == MMRM_CLIENT_CLOCK) {
- /* check for skip mmrm */
- CHECK_SKIP_MMRM_CLK_RSRC(drv_data);
- rc = mmrm_clk_client_getval(drv_data->clk_mgr,
- client, val);
- if (rc != 0) {
- d_mpr_e("%s: failed to get value for client\n", __func__);
- goto err_exit;
- }
- } else {
- d_mpr_e("%s: unknown client_type %d\n",
- __func__, client->client_type);
- }
- skip_mmrm:
- return rc;
- err_exit:
- d_mpr_e("%s: error = %d\n", __func__, rc);
- return rc;
- }
- EXPORT_SYMBOL(mmrm_client_get_value);
- int mmrm_client_get_clk_count(void)
- {
- struct mmrm_sw_clk_mgr_info *sinfo;
- if (drv_data == (void *) -EPROBE_DEFER)
- return 0;
- sinfo = &(drv_data->clk_mgr->data.sw_info);
- return sinfo->tot_clk_clients;
- }
- EXPORT_SYMBOL(mmrm_client_get_clk_count);
- static int sysfs_get_param(const char *buf, u32 *param)
- {
- int base;
- if (buf) {
- if ((buf[1] == 'x') || (buf[1] == 'X'))
- base = 16;
- else
- base = 10;
- if (kstrtou32(buf, base, param) != 0)
- return -EINVAL;
- }
- return 0;
- }
- static ssize_t mmrm_sysfs_debug_get(struct device *dev,
- struct device_attribute *attr, char *buf)
- {
- int ret;
- ret = scnprintf(buf, MMRM_SYSFS_ENTRY_MAX_LEN, "0x%x\n", msm_mmrm_debug);
- pr_info("%s: 0x%04X\n", __func__, msm_mmrm_debug);
- return ret;
- }
- static ssize_t mmrm_sysfs_debug_set(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
- {
- int ret;
- u32 reg_addr;
- ret = sysfs_get_param(buf, ®_addr);
- if (ret == 0)
- msm_mmrm_debug = reg_addr;
- return count;
- }
- static ssize_t mmrm_sysfs_enable_throttle_get(struct device *dev,
- struct device_attribute *attr, char *buf)
- {
- int ret;
- ret = scnprintf(buf, MMRM_SYSFS_ENTRY_MAX_LEN, "0x%x\n", msm_mmrm_enable_throttle_feature);
- pr_info("%s: 0x%04X\n", __func__, msm_mmrm_enable_throttle_feature);
- return ret;
- }
- static ssize_t mmrm_sysfs_enable_throttle_set(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
- {
- u32 reg_addr;
- int ret;
- ret = sysfs_get_param(buf, ®_addr);
- if (ret == 0)
- msm_mmrm_enable_throttle_feature = (u8)reg_addr;
- return count;
- }
- static ssize_t mmrm_sysfs_allow_multiple_get(struct device *dev,
- struct device_attribute *attr, char *buf)
- {
- int ret;
- ret = scnprintf(buf, MMRM_SYSFS_ENTRY_MAX_LEN, "0x%x\n", msm_mmrm_allow_multiple_register);
- pr_info("%s: 0x%04X\n", __func__, msm_mmrm_allow_multiple_register);
- return ret;
- }
- static ssize_t mmrm_sysfs_allow_multiple_set(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
- {
- u32 reg_addr;
- int ret;
- ret = sysfs_get_param(buf, ®_addr);
- if (ret == 0)
- msm_mmrm_allow_multiple_register = (u8)reg_addr;
- return count;
- }
- static ssize_t dump_enabled_client_info_show(struct device *dev,
- struct device_attribute *attr, char *buf)
- {
- int rc;
- rc = mmrm_clk_print_enabled_client_info(drv_data->clk_mgr, buf, MMRM_SYSFS_ENTRY_MAX_LEN);
- if (rc == 0)
- d_mpr_e("%s: failed to dump client info\n", __func__);
- return rc;
- }
- static ssize_t dump_clk_res_info_show(struct device *dev,
- struct device_attribute *attr, char *buf)
- {
- int i, len;
- struct mmrm_clk_platform_resources *cres = &drv_data->clk_res;
- struct nom_clk_src_set *clk_set = &cres->nom_clk_set;
- struct nom_clk_src_info *pclk;
- int left_spaces = MMRM_SYSFS_ENTRY_MAX_LEN;
- len = scnprintf(buf, left_spaces, "threshold: %d\n",
- cres->peak_threshold);
- left_spaces -= len;
- buf += len;
- for (i = 0, pclk = clk_set->clk_src_tbl; i < clk_set->count && left_spaces > 1; i++, pclk++) {
- len = scnprintf(buf, left_spaces, "%d\t%d\t% 8d\t%d\t%d\n",
- pclk->domain,
- pclk->clk_src_id,
- pclk->nom_dyn_pwr,
- pclk->nom_leak_pwr,
- pclk->num_hw_block);
- left_spaces -= len;
- buf += len;
- }
- return MMRM_SYSFS_ENTRY_MAX_LEN - left_spaces;
- }
- static DEVICE_ATTR(debug, 0644,
- mmrm_sysfs_debug_get,
- mmrm_sysfs_debug_set);
- static DEVICE_ATTR(enable_throttle_feature, 0644,
- mmrm_sysfs_enable_throttle_get,
- mmrm_sysfs_enable_throttle_set);
- static DEVICE_ATTR(allow_multiple_register, 0644,
- mmrm_sysfs_allow_multiple_get,
- mmrm_sysfs_allow_multiple_set);
- static DEVICE_ATTR_RO(dump_enabled_client_info);
- static DEVICE_ATTR_RO(dump_clk_res_info);
- static struct attribute *mmrm_fs_attrs[] = {
- &dev_attr_debug.attr,
- &dev_attr_enable_throttle_feature.attr,
- &dev_attr_allow_multiple_register.attr,
- &dev_attr_dump_enabled_client_info.attr,
- &dev_attr_dump_clk_res_info.attr,
- NULL,
- };
- static struct attribute_group mmrm_fs_attrs_group = {
- .attrs = mmrm_fs_attrs,
- };
- static int msm_mmrm_probe_init(struct platform_device *pdev)
- {
- int rc = 0;
- u32 clk_clients = 0;
- drv_data = kzalloc(sizeof(*drv_data), GFP_KERNEL);
- if (!drv_data) {
- d_mpr_e("%s: unable to allocate memory for mmrm driver\n",
- __func__);
- rc = -ENOMEM;
- goto err_no_mem;
- }
- /* check for clk clients needing admission control */
- clk_clients = mmrm_count_clk_clients_frm_dt(pdev);
- if (clk_clients) {
- d_mpr_h("%s: %d clk clients managed for admission control\n",
- __func__, clk_clients);
- drv_data->is_clk_scaling_supported = true;
- } else {
- d_mpr_h("%s: no clk clients managed for admission control\n",
- __func__);
- drv_data->is_clk_scaling_supported = false;
- goto skip_mmrm;
- }
- drv_data->platform_data = mmrm_get_platform_data(&pdev->dev);
- if (!drv_data->platform_data) {
- d_mpr_e("%s: unable to get platform data\n",
- __func__);
- rc = -EINVAL;
- goto err_get_drv_data;
- }
- drv_data->debugfs_root = msm_mmrm_debugfs_init();
- if (!drv_data->debugfs_root)
- d_mpr_e("%s: failed to create debugfs for mmrm\n", __func__);
- dev_set_drvdata(&pdev->dev, drv_data);
- rc = mmrm_read_platform_resources(pdev, drv_data);
- if (rc) {
- d_mpr_e("%s: unable to read platform resources for mmrm\n",
- __func__);
- goto err_read_pltfrm_rsc;
- }
- rc = mmrm_init(drv_data);
- if (rc) {
- d_mpr_e("%s: failed to init mmrm\n",
- __func__);
- goto err_mmrm_init;
- }
- if (sysfs_create_group(&pdev->dev.kobj, &mmrm_fs_attrs_group)) {
- d_mpr_e("%s: failed to create sysfs\n",
- __func__);
- }
- skip_mmrm:
- return rc;
- err_mmrm_init:
- msm_mmrm_debugfs_deinit(drv_data->debugfs_root);
- err_read_pltfrm_rsc:
- mmrm_free_platform_resources(drv_data);
- err_get_drv_data:
- RESET_DRV_DATA(drv_data);
- err_no_mem:
- d_mpr_e("%s: error = %d\n", __func__, rc);
- return rc;
- }
- static int msm_mmrm_probe(struct platform_device *pdev)
- {
- int rc = -EINVAL;
- d_mpr_h("%s\n", __func__);
- VERIFY_PDEV(pdev)
- if (of_device_is_compatible(pdev->dev.of_node, "qcom,msm-mmrm"))
- return msm_mmrm_probe_init(pdev);
- d_mpr_e("%s: no compatible device node\n", __func__);
- return rc;
- err_exit:
- d_mpr_e("%s: error = %d\n", __func__, rc);
- return rc;
- }
- static int msm_mmrm_remove(struct platform_device *pdev)
- {
- int rc = 0;
- VERIFY_PDEV(pdev);
- drv_data = dev_get_drvdata(&pdev->dev);
- if (!drv_data) {
- d_mpr_e("%s: null driver data\n", __func__);
- return -EINVAL;
- }
- if (drv_data->is_clk_scaling_supported) {
- sysfs_remove_group(&pdev->dev.kobj, &mmrm_fs_attrs_group);
- msm_mmrm_debugfs_deinit(drv_data->debugfs_root);
- mmrm_deinit(drv_data);
- mmrm_free_platform_resources(drv_data);
- }
- dev_set_drvdata(&pdev->dev, NULL);
- RESET_DRV_DATA(drv_data);
- return rc;
- err_exit:
- d_mpr_e("%s: error = %d\n", __func__, rc);
- return rc;
- }
- static const struct of_device_id msm_mmrm_dt_match[] = {
- {.compatible = "qcom,msm-mmrm"},
- {}
- };
- MODULE_DEVICE_TABLE(of, msm_mmrm_dt_match);
- static struct platform_driver msm_mmrm_driver = {
- .probe = msm_mmrm_probe,
- .remove = msm_mmrm_remove,
- .driver = {
- .name = "msm-mmrm",
- .of_match_table = msm_mmrm_dt_match,
- },
- };
- static int __init msm_mmrm_init(void)
- {
- int rc = 0;
- rc = platform_driver_register(&msm_mmrm_driver);
- if (rc) {
- d_mpr_e("%s: failed to register platform driver\n",
- __func__);
- goto err_platform_drv_reg;
- }
- d_mpr_h("%s: success\n", __func__);
- return rc;
- err_platform_drv_reg:
- d_mpr_e("%s: error = %d\n", __func__, rc);
- return rc;
- }
- static void __exit msm_mmrm_exit(void)
- {
- platform_driver_unregister(&msm_mmrm_driver);
- }
- module_init(msm_mmrm_init);
- module_exit(msm_mmrm_exit);
- MODULE_DESCRIPTION("QTI MMRM Driver");
- MODULE_LICENSE("GPL v2");
|