18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Simple heartbeat STM source driver
48c2ecf20Sopenharmony_ci * Copyright (c) 2016, Intel Corporation.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Heartbeat STM source will send repetitive messages over STM devices to a
78c2ecf20Sopenharmony_ci * trace host.
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/kernel.h>
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci#include <linux/hrtimer.h>
138c2ecf20Sopenharmony_ci#include <linux/slab.h>
148c2ecf20Sopenharmony_ci#include <linux/stm.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#define STM_HEARTBEAT_MAX	32
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_cistatic int nr_devs = 4;
198c2ecf20Sopenharmony_cistatic int interval_ms = 10;
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cimodule_param(nr_devs, int, 0400);
228c2ecf20Sopenharmony_cimodule_param(interval_ms, int, 0600);
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_cistatic struct stm_heartbeat {
258c2ecf20Sopenharmony_ci	struct stm_source_data	data;
268c2ecf20Sopenharmony_ci	struct hrtimer		hrtimer;
278c2ecf20Sopenharmony_ci	unsigned int		active;
288c2ecf20Sopenharmony_ci} stm_heartbeat[STM_HEARTBEAT_MAX];
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistatic const char str[] = "heartbeat stm source driver is here to serve you";
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistatic enum hrtimer_restart stm_heartbeat_hrtimer_handler(struct hrtimer *hr)
338c2ecf20Sopenharmony_ci{
348c2ecf20Sopenharmony_ci	struct stm_heartbeat *heartbeat = container_of(hr, struct stm_heartbeat,
358c2ecf20Sopenharmony_ci						       hrtimer);
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	stm_source_write(&heartbeat->data, 0, str, sizeof str);
388c2ecf20Sopenharmony_ci	if (heartbeat->active)
398c2ecf20Sopenharmony_ci		hrtimer_forward_now(hr, ms_to_ktime(interval_ms));
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	return heartbeat->active ? HRTIMER_RESTART : HRTIMER_NORESTART;
428c2ecf20Sopenharmony_ci}
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cistatic int stm_heartbeat_link(struct stm_source_data *data)
458c2ecf20Sopenharmony_ci{
468c2ecf20Sopenharmony_ci	struct stm_heartbeat *heartbeat =
478c2ecf20Sopenharmony_ci		container_of(data, struct stm_heartbeat, data);
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	heartbeat->active = 1;
508c2ecf20Sopenharmony_ci	hrtimer_start(&heartbeat->hrtimer, ms_to_ktime(interval_ms),
518c2ecf20Sopenharmony_ci		      HRTIMER_MODE_ABS);
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	return 0;
548c2ecf20Sopenharmony_ci}
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistatic void stm_heartbeat_unlink(struct stm_source_data *data)
578c2ecf20Sopenharmony_ci{
588c2ecf20Sopenharmony_ci	struct stm_heartbeat *heartbeat =
598c2ecf20Sopenharmony_ci		container_of(data, struct stm_heartbeat, data);
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	heartbeat->active = 0;
628c2ecf20Sopenharmony_ci	hrtimer_cancel(&heartbeat->hrtimer);
638c2ecf20Sopenharmony_ci}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic int stm_heartbeat_init(void)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	int i, ret;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	if (nr_devs < 0 || nr_devs > STM_HEARTBEAT_MAX)
708c2ecf20Sopenharmony_ci		return -EINVAL;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	for (i = 0; i < nr_devs; i++) {
738c2ecf20Sopenharmony_ci		stm_heartbeat[i].data.name =
748c2ecf20Sopenharmony_ci			kasprintf(GFP_KERNEL, "heartbeat.%d", i);
758c2ecf20Sopenharmony_ci		if (!stm_heartbeat[i].data.name) {
768c2ecf20Sopenharmony_ci			ret = -ENOMEM;
778c2ecf20Sopenharmony_ci			goto fail_unregister;
788c2ecf20Sopenharmony_ci		}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci		stm_heartbeat[i].data.nr_chans	= 1;
818c2ecf20Sopenharmony_ci		stm_heartbeat[i].data.link	= stm_heartbeat_link;
828c2ecf20Sopenharmony_ci		stm_heartbeat[i].data.unlink	= stm_heartbeat_unlink;
838c2ecf20Sopenharmony_ci		hrtimer_init(&stm_heartbeat[i].hrtimer, CLOCK_MONOTONIC,
848c2ecf20Sopenharmony_ci			     HRTIMER_MODE_ABS);
858c2ecf20Sopenharmony_ci		stm_heartbeat[i].hrtimer.function =
868c2ecf20Sopenharmony_ci			stm_heartbeat_hrtimer_handler;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci		ret = stm_source_register_device(NULL, &stm_heartbeat[i].data);
898c2ecf20Sopenharmony_ci		if (ret)
908c2ecf20Sopenharmony_ci			goto fail_free;
918c2ecf20Sopenharmony_ci	}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	return 0;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cifail_unregister:
968c2ecf20Sopenharmony_ci	for (i--; i >= 0; i--) {
978c2ecf20Sopenharmony_ci		stm_source_unregister_device(&stm_heartbeat[i].data);
988c2ecf20Sopenharmony_cifail_free:
998c2ecf20Sopenharmony_ci		kfree(stm_heartbeat[i].data.name);
1008c2ecf20Sopenharmony_ci	}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	return ret;
1038c2ecf20Sopenharmony_ci}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_cistatic void stm_heartbeat_exit(void)
1068c2ecf20Sopenharmony_ci{
1078c2ecf20Sopenharmony_ci	int i;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	for (i = 0; i < nr_devs; i++) {
1108c2ecf20Sopenharmony_ci		stm_source_unregister_device(&stm_heartbeat[i].data);
1118c2ecf20Sopenharmony_ci		kfree(stm_heartbeat[i].data.name);
1128c2ecf20Sopenharmony_ci	}
1138c2ecf20Sopenharmony_ci}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_cimodule_init(stm_heartbeat_init);
1168c2ecf20Sopenharmony_cimodule_exit(stm_heartbeat_exit);
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
1198c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("stm_heartbeat driver");
1208c2ecf20Sopenharmony_ciMODULE_AUTHOR("Alexander Shishkin <alexander.shishkin@linux.intel.com>");
121