Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 67d4b7ef authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "drivers: thermal: Add limits cdsp Isense driver"

parents 9d630cbe 3cef91c3
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -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.
+1 −0
Original line number Diff line number Diff line
@@ -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
+179 −0
Original line number Diff line number Diff line
// 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);