drivers: thermal: Add limits cdsp Isense driver

Add driver to read limits cdsp Isense calibration data from SMEM
and enable sysfs file support to userspace daemon to access this data.
This driver is needed to avoid multiple re-calibration.

Change-Id: I1f1a70937034db388297c1ab52e12dcc468ee1dd
Signed-off-by: Manaf Meethalavalappu Pallikunhi <manafm@codeaurora.org>
This commit is contained in:
Manaf Meethalavalappu Pallikunhi 2019-09-11 22:29:16 +05:30 • committed by Gerrit - the friendly Code Review server
commit 3cef91c38e
3 changed files with 189 additions and 0 deletions

View file

@ -136,3 +136,12 @@ config QTI_RPM_SMD_COOLING_DEVICE
level to RPM hardware via SMD protocol. This mitigation device
will be used by temperature reliability rules to restrict a
railway at predefined voltage corner using RPM hardware.
config QTI_LIMITS_ISENSE_CDSP
tristate "QTI CDSP Limits Isense Driver"
depends on QCOM_SMEM
help
This enables driver to read cdsp isense calibration data from
shared memory and enable sysfs file support to access this data. This
driver is required for the chipsets where isense hardware is present
as part of cdsp subsystem.

View file

@ -21,3 +21,4 @@ obj-$(CONFIG_QTI_POLICY_ENGINE_SENSOR) += policy_engine.o
obj-${CONFIG_QTI_SDPM_CLOCK_MONITOR} += sdpm_clk.o
obj-$(CONFIG_REGULATOR_COOLING_DEVICE) += regulator_cdev.o
obj-$(CONFIG_QTI_RPM_SMD_COOLING_DEVICE) += rpm_smd_cooling_device.o
obj-$(CONFIG_QTI_LIMITS_ISENSE_CDSP) += msm_isense_cdsp.o

View file

@ -0,0 +1,179 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
*Copyright (c) 2019, The Linux Foundation. All rights reserved.
*/
#define pr_fmt(fmt) "%s:%s " fmt, KBUILD_MODNAME, __func__
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/soc/qcom/smem.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>
#define LIMITS_SMEM_CONFIG 619
#define PARTITION_SIZE_BYTES 4096
struct limits_isense_cdsp_sysfs {
struct kobj_attribute attr;
struct module_kobject *m_kobj;
};
struct limits_isense_cdsp_smem_data {
uint8_t subsys_cal_done;
uint8_t store_data_in_partition;
uint16_t size_of_partition_data;
} __packed;
static struct limits_isense_cdsp_smem_data *limits_isense_cdsp_data;
static struct limits_isense_cdsp_sysfs *limits_isense_cdsp_sysfs;
static ssize_t limits_isense_cdsp_data_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
if (!limits_isense_cdsp_data)
return -ENODATA;
if (!limits_isense_cdsp_data->subsys_cal_done)
return -EAGAIN;
if (limits_isense_cdsp_data->store_data_in_partition) {
if (limits_isense_cdsp_data->size_of_partition_data >
PARTITION_SIZE_BYTES)
return -ENOBUFS;
memcpy(buf,
((void *)
(&limits_isense_cdsp_data->size_of_partition_data) + 2),
limits_isense_cdsp_data->size_of_partition_data);
return limits_isense_cdsp_data->size_of_partition_data;
}
return -ENODATA;
}
static int limits_create_msm_limits_cdsp_sysfs(struct platform_device *pdev)
{
int err = 0;
struct module_kobject *m_kobj;
limits_isense_cdsp_sysfs = devm_kcalloc(&pdev->dev, 1,
sizeof(*limits_isense_cdsp_sysfs), GFP_KERNEL);
if (!limits_isense_cdsp_sysfs)
return PTR_ERR(limits_isense_cdsp_sysfs);
m_kobj = devm_kcalloc(&pdev->dev, 1,
sizeof(*m_kobj), GFP_KERNEL);
if (!m_kobj)
return PTR_ERR(m_kobj);
limits_isense_cdsp_sysfs->m_kobj = m_kobj;
m_kobj->mod = THIS_MODULE;
m_kobj->kobj.kset = module_kset;
err = kobject_init_and_add(&m_kobj->kobj, &module_ktype, NULL,
"%s", KBUILD_MODNAME);
if (err) {
dev_err(&pdev->dev,
"%s: cannot create kobject for %s\n",
__func__, KBUILD_MODNAME);
goto exit_handler;
}
kobject_get(&m_kobj->kobj);
if (IS_ERR_OR_NULL(&m_kobj->kobj)) {
err = PTR_ERR(&m_kobj->kobj);
goto exit_handler;
}
sysfs_attr_init(&limits_isense_cdsp_sysfs->attr.attr);
limits_isense_cdsp_sysfs->attr.attr.name = "data";
limits_isense_cdsp_sysfs->attr.attr.mode = 0444;
limits_isense_cdsp_sysfs->attr.show = limits_isense_cdsp_data_show;
limits_isense_cdsp_sysfs->attr.store = NULL;
sysfs_create_file(&m_kobj->kobj, &limits_isense_cdsp_sysfs->attr.attr);
if (err) {
dev_err(&pdev->dev, "cannot create sysfs file\n");
goto exit_handler;
}
return err;
exit_handler:
kobject_del(&m_kobj->kobj);
return err;
}
static int limits_isense_cdsp_probe(struct platform_device *pdev)
{
void *smem_ptr = NULL;
size_t size = 0;
int err;
if (limits_isense_cdsp_data)
return 0;
smem_ptr = qcom_smem_get(QCOM_SMEM_HOST_ANY, LIMITS_SMEM_CONFIG, &size);
if (!smem_ptr || !size) {
dev_err(&pdev->dev,
"Failed to get limits SMEM Address\n");
return PTR_ERR(smem_ptr);
}
limits_isense_cdsp_data =
(struct limits_isense_cdsp_smem_data *) smem_ptr;
err = limits_create_msm_limits_cdsp_sysfs(pdev);
return err;
}
static int limits_isense_cdsp_remove(struct platform_device *pdev)
{
limits_isense_cdsp_data = NULL;
if (limits_isense_cdsp_sysfs && limits_isense_cdsp_sysfs->m_kobj) {
sysfs_remove_file(&limits_isense_cdsp_sysfs->m_kobj->kobj,
&limits_isense_cdsp_sysfs->attr.attr);
kobject_del(&limits_isense_cdsp_sysfs->m_kobj->kobj);
}
return 0;
}
static const struct of_device_id limits_isense_cdsp_match[] = {
{ .compatible = "qcom,msm-limits-cdsp", },
{},
};
static struct platform_driver limits_isense_cdsp_driver = {
.probe = limits_isense_cdsp_probe,
.remove = limits_isense_cdsp_remove,
.driver = {
.name = KBUILD_MODNAME,
.of_match_table = limits_isense_cdsp_match,
},
};
int __init limits_isense_cdsp_late_init(void)
{
int err;
err = platform_driver_register(&limits_isense_cdsp_driver);
if (err)
pr_err("Failed to register limits_isense_cdsp platform driver: %d\n",
err);
return err;
}
late_initcall(limits_isense_cdsp_late_init);