207 lines
5.1 KiB
C
207 lines
5.1 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (C) 2021 MediaTek Inc.
|
|
*/
|
|
|
|
#include <linux/device.h>
|
|
#include <linux/module.h>
|
|
#include <linux/of.h>
|
|
#include <linux/scmi_protocol.h>
|
|
|
|
#include "tinysys-scmi.h"
|
|
|
|
|
|
static struct scmi_tinysys_info_st *t_info;
|
|
static const struct scmi_tinysys_proto_ops *tinysys_ops;
|
|
|
|
f_handler_t cb_array[SCMI_TINYSYS_CB_MAX];
|
|
|
|
struct scmi_tinysys_info_st *get_scmi_tinysys_info(void)
|
|
{
|
|
return t_info;
|
|
}
|
|
EXPORT_SYMBOL(get_scmi_tinysys_info);
|
|
|
|
int scmi_tinysys_common_set(const struct scmi_protocol_handle *ph, u32 feature_id,
|
|
u32 p1, u32 p2, u32 p3, u32 p4, u32 p5)
|
|
{
|
|
return tinysys_ops->common_set(ph, feature_id, p1, p2, p3, p4, p5);
|
|
}
|
|
EXPORT_SYMBOL(scmi_tinysys_common_set);
|
|
|
|
int scmi_tinysys_common_get(const struct scmi_protocol_handle *ph, u32 feature_id,
|
|
u32 p1, struct scmi_tinysys_status *rvalue)
|
|
{
|
|
return tinysys_ops->common_get(ph, feature_id, p1, rvalue);
|
|
}
|
|
EXPORT_SYMBOL(scmi_tinysys_common_get);
|
|
|
|
int scmi_tinysys_notifier_fn(struct notifier_block *nb,
|
|
unsigned long action, void *data)
|
|
{
|
|
struct scmi_tinysys_notifier_report *r = data;
|
|
scmi_tinysys_report *report = (scmi_tinysys_report *)&(r->f_id);
|
|
f_handler_t func;
|
|
|
|
/*
|
|
pr_notice("scmi notify report ktime:%ld f_id:%d p1:%d %d %d %d\n",
|
|
r->timestamp, r->f_id, r->p1_status, r->p2_status, r->p3_status,
|
|
r->p4_status);
|
|
*/
|
|
if (r->f_id < SCMI_TINYSYS_CB_MAX) {
|
|
|
|
func = cb_array[r->f_id];
|
|
if (func)
|
|
func(r->f_id, report);
|
|
}
|
|
return NOTIFY_OK;
|
|
}
|
|
|
|
static struct notifier_block tinysys_nb = {
|
|
.notifier_call = scmi_tinysys_notifier_fn,
|
|
};
|
|
|
|
int scmi_tinysys_event_notify(u32 feature_id, u32 notify_enable)
|
|
{
|
|
|
|
int ret = 0;
|
|
int f_id = feature_id;
|
|
struct scmi_device *sdev = t_info->sdev;
|
|
|
|
if (notify_enable) {
|
|
ret = sdev->handle->notify_ops->devm_event_notifier_register(sdev,
|
|
SCMI_PROTOCOL_TINYSYS, SCMI_EVENT_TINYSYS_NOTIFIER, &f_id, &tinysys_nb);
|
|
if (ret)
|
|
pr_notice("scmi register_event_notifier f_id:%d ret:%d\n", f_id, ret);
|
|
|
|
} else {
|
|
ret = sdev->handle->notify_ops->devm_event_notifier_unregister(sdev,
|
|
SCMI_PROTOCOL_TINYSYS, SCMI_EVENT_TINYSYS_NOTIFIER, &f_id, &tinysys_nb);
|
|
if (ret)
|
|
pr_notice("scmi unregister_event_notifier f_id:%d ret:%d\n", f_id, ret);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL(scmi_tinysys_event_notify);
|
|
|
|
void scmi_tinysys_register_event_notifier(u32 feature_id, f_handler_t hand)
|
|
{
|
|
if (feature_id < SCMI_TINYSYS_CB_MAX)
|
|
cb_array[feature_id] = hand;
|
|
else
|
|
pr_notice("feature_id %d >= SCMI_TINYSYS_CB_MAX\n", feature_id);
|
|
}
|
|
EXPORT_SYMBOL(scmi_tinysys_register_event_notifier);
|
|
|
|
#ifdef TINYSYS_SCMI_DEBUG
|
|
void test_callback(u32 feature_id, scmi_tinysys_report *r)
|
|
{
|
|
pr_notice("scmi %s: %d, %x %x %x %x %x\n", __func__,
|
|
feature_id, r->feature_id, r->p1, r->p2, r->p3, r->p4);
|
|
}
|
|
|
|
static ssize_t tinysys_scmi_debug_store(struct device *kobj,
|
|
struct device_attribute *attr, const char *buf, size_t n)
|
|
{
|
|
|
|
int ret;
|
|
int pro_id, f_id, p1, p2, p3, p4, p5;
|
|
struct scmi_tinysys_info_st *tt;
|
|
struct scmi_tinysys_status rvalue;
|
|
char *prompt = "SCMI:";
|
|
|
|
if (sscanf(buf, "%d %d %d %d %d %d %d", &pro_id, &f_id, &p1, &p2, &p3, &p4, &p5) != 7)
|
|
return -EINVAL;
|
|
pr_notice("%s pro_id:%d f_id:%d para:%d %d %d %d %d\n", prompt,
|
|
pro_id, f_id, p1, p2, p3, p4, p5);
|
|
|
|
tt = get_scmi_tinysys_info();
|
|
|
|
switch (pro_id) {
|
|
case 0:
|
|
ret = scmi_tinysys_common_set(tt->ph, f_id, p1, p2, p3, p4, p5);
|
|
if (ret)
|
|
pr_notice("%s ret = %d\n", prompt, ret);
|
|
break;
|
|
case 1:
|
|
ret = scmi_tinysys_common_get(tt->ph, f_id, p1, &rvalue);
|
|
if (ret)
|
|
pr_notice("%s scmi_tinysys_common_get error ret = %d\n", prompt, ret);
|
|
else
|
|
pr_notice("%s scmi_tinysys_common_get r1:%d r2:%d r3:%d\n", prompt,
|
|
rvalue.r1, rvalue.r2, rvalue.r3);
|
|
break;
|
|
case 2:
|
|
scmi_tinysys_register_event_notifier(f_id, (f_handler_t)test_callback);
|
|
ret = scmi_tinysys_event_notify(f_id, 1);
|
|
break;
|
|
case 3:
|
|
ret = scmi_tinysys_event_notify(f_id, 0);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return n;
|
|
|
|
}
|
|
|
|
static ssize_t tinysys_scmi_debug_show(struct device *dev
|
|
, struct device_attribute *attr, char *buf)
|
|
{
|
|
return scnprintf(buf, PAGE_SIZE, "%s\n", __func__);
|
|
}
|
|
|
|
DEVICE_ATTR_RW(tinysys_scmi_debug);
|
|
#endif
|
|
|
|
|
|
static int scmi_tinysys_probe(struct scmi_device *sdev)
|
|
{
|
|
struct device *dev = &sdev->dev;
|
|
|
|
const struct scmi_handle *handle = sdev->handle;
|
|
struct scmi_protocol_handle *ph;
|
|
|
|
if (!handle)
|
|
return -ENODEV;
|
|
|
|
scmi_tinysys_register();
|
|
|
|
tinysys_ops = handle->devm_protocol_get(sdev, SCMI_PROTOCOL_TINYSYS, &ph);
|
|
if (IS_ERR(tinysys_ops))
|
|
return PTR_ERR(tinysys_ops);
|
|
|
|
t_info = devm_kzalloc(dev, sizeof(*t_info), GFP_KERNEL);
|
|
if (!t_info)
|
|
return -ENOMEM;
|
|
|
|
t_info->sdev = sdev;
|
|
|
|
t_info->ph = ph;
|
|
|
|
#ifdef TINYSYS_SCMI_DEBUG
|
|
if (device_create_file(dev, &dev_attr_tinysys_scmi_debug))
|
|
pr_notice("tinysys scmi debug ret fail\n");
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct scmi_device_id scmi_id_table[] = {
|
|
{ SCMI_PROTOCOL_TINYSYS, "tinysys" },
|
|
{ },
|
|
};
|
|
MODULE_DEVICE_TABLE(scmi, scmi_id_table);
|
|
|
|
static struct scmi_driver scmi_tinysys_driver = {
|
|
.name = "scmi-tinysys",
|
|
.probe = scmi_tinysys_probe,
|
|
.id_table = scmi_id_table,
|
|
};
|
|
module_scmi_driver(scmi_tinysys_driver);
|
|
|
|
MODULE_DESCRIPTION("SCMI tinysys driver");
|
|
MODULE_LICENSE("GPL v2");
|
|
|