sdk-hwV1.3/lichee/linux-4.9/drivers/rpmsg/rpmsg_client_heart.c

178 lines
4.2 KiB
C

/*
* Remote processor messaging - sample heartbeat client driver
*
* Copyright (C) 2011 Texas Instruments, Inc.
* Copyright (C) 2011 Allwinner, Inc.
*
* Ohad Ben-Cohen <ohad@wizery.com>
* Brian Swetland <swetland@google.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/rpmsg.h>
#include <linux/workqueue.h>
#include <linux/remoteproc.h>
#include <linux/timer.h>
#include "linux/types.h"
#define HEART_RATE (2 * HZ)
#define TICK_INTERVAL 100
extern int sunxi_rproc_report_crash(char *name, enum rproc_crash_type type);
struct rpmsg_client_heartbeat {
struct rpmsg_device *rpdev;
struct timer_list timer;
uint32_t tick;
char master[32];
u8 sleep;
};
struct hearbeat_packet {
char name[32];
uint32_t tick;
};
static void time_out_handler(unsigned long arg)
{
struct rpmsg_client_heartbeat *idata =
(struct rpmsg_client_heartbeat *)arg;
if (!arg)
return;
if (idata->sleep)
return;
sunxi_rproc_report_crash(idata->master, RPROC_WATCHDOG);
}
static int rpmsg_heartbeat_cb(struct rpmsg_device *rpdev, void *data, int len,
void *priv, u32 src)
{
struct rpmsg_client_heartbeat *idata = dev_get_drvdata(&rpdev->dev);
struct hearbeat_packet *pack = data;
dev_dbg(&rpdev->dev, "%s heartbeat: %d\n", pack->name, pack->tick);
if (idata->master[0] == '\0') {
memcpy(idata->master, pack->name, 32);
idata->master[31] = '\0';
}
if (idata->sleep) {
dev_err(&rpdev->dev, "%s invalid heartbeat\n", pack->name);
return 0;
}
if (idata->tick != pack->tick)
idata->tick = pack->tick;
idata->tick++;
idata->tick %= TICK_INTERVAL;
mod_timer(&idata->timer, jiffies + HEART_RATE);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int sunxi_rpmsg_hearbeat_suspend(struct device *dev)
{
struct rpmsg_client_heartbeat *idata = NULL;
idata = dev_get_drvdata(dev);
idata->sleep = 1;
del_timer_sync(&idata->timer);
return 0;
}
static int sunxi_rpmsg_hearbeat_resume(struct device *dev)
{
struct rpmsg_client_heartbeat *idata = NULL;
idata = dev_get_drvdata(dev);
idata->timer.function = time_out_handler;
idata->timer.data = (unsigned long)idata;
idata->timer.expires = jiffies + 10 * HEART_RATE;
add_timer(&idata->timer);
idata->sleep = 0;
return 0;
}
static struct dev_pm_ops sunxi_rpmsg_hearbeat_pm_ops = {
.suspend = sunxi_rpmsg_hearbeat_suspend,
.resume = sunxi_rpmsg_hearbeat_resume,
};
#endif
static int rpmsg_heartbeat_probe(struct rpmsg_device *rpdev)
{
struct rpmsg_client_heartbeat *idata;
idata = devm_kzalloc(&rpdev->dev, sizeof(*idata), GFP_KERNEL);
if (!idata)
return -ENOMEM;
idata->rpdev = rpdev;
idata->tick = 0;
init_timer(&idata->timer);
idata->timer.function = time_out_handler;
idata->timer.data = (unsigned long)idata;
idata->timer.expires = 3 * HEART_RATE;
idata->sleep = 0;
dev_set_drvdata(&rpdev->dev, idata);
/* wo need to announce the new ept to remote */
rpdev->announce = rpdev->src != RPMSG_ADDR_ANY;
add_timer(&idata->timer);
return 0;
}
static void rpmsg_heartbeat_remove(struct rpmsg_device *rpdev)
{
struct rpmsg_client_heartbeat *idata = dev_get_drvdata(&rpdev->dev);
dev_info(&rpdev->dev, "%s is removed\n", dev_name(&rpdev->dev));
del_timer_sync(&idata->timer);
}
static struct rpmsg_device_id rpmsg_driver_hearbeat_id_table[] = {
{ .name = "sunxi,rpmsg_heartbeat" },
{ },
};
MODULE_DEVICE_TABLE(rpmsg, rpmsg_driver_hearbeat_id_table);
static struct rpmsg_driver rpmsg_sample_client = {
.drv = {
.name = KBUILD_MODNAME,
#ifdef CONFIG_PM_SLEEP
.pm = &sunxi_rpmsg_hearbeat_pm_ops,
#endif
},
.id_table = rpmsg_driver_hearbeat_id_table,
.probe = rpmsg_heartbeat_probe,
.callback = rpmsg_heartbeat_cb,
.remove = rpmsg_heartbeat_remove,
};
module_rpmsg_driver(rpmsg_sample_client);
MODULE_DESCRIPTION("Remote processor messaging sample client driver");
MODULE_LICENSE("GPL v2");