19ea393d8SAlexander Shishkin // SPDX-License-Identifier: GPL-2.0
211929185SAlexander Shishkin /*
311929185SAlexander Shishkin  * Simple heartbeat STM source driver
411929185SAlexander Shishkin  * Copyright (c) 2016, Intel Corporation.
511929185SAlexander Shishkin  *
611929185SAlexander Shishkin  * Heartbeat STM source will send repetitive messages over STM devices to a
711929185SAlexander Shishkin  * trace host.
811929185SAlexander Shishkin  */
911929185SAlexander Shishkin 
1011929185SAlexander Shishkin #include <linux/kernel.h>
1111929185SAlexander Shishkin #include <linux/module.h>
1211929185SAlexander Shishkin #include <linux/hrtimer.h>
1311929185SAlexander Shishkin #include <linux/slab.h>
1411929185SAlexander Shishkin #include <linux/stm.h>
1511929185SAlexander Shishkin 
1611929185SAlexander Shishkin #define STM_HEARTBEAT_MAX	32
1711929185SAlexander Shishkin 
1811929185SAlexander Shishkin static int nr_devs = 4;
1911929185SAlexander Shishkin static int interval_ms = 10;
2011929185SAlexander Shishkin 
21c8be4899SAlexander Shishkin module_param(nr_devs, int, 0400);
2211929185SAlexander Shishkin module_param(interval_ms, int, 0600);
2311929185SAlexander Shishkin 
2411929185SAlexander Shishkin static struct stm_heartbeat {
2511929185SAlexander Shishkin 	struct stm_source_data	data;
2611929185SAlexander Shishkin 	struct hrtimer		hrtimer;
2711929185SAlexander Shishkin 	unsigned int		active;
2811929185SAlexander Shishkin } stm_heartbeat[STM_HEARTBEAT_MAX];
2911929185SAlexander Shishkin 
3011929185SAlexander Shishkin static const char str[] = "heartbeat stm source driver is here to serve you";
3111929185SAlexander Shishkin 
stm_heartbeat_hrtimer_handler(struct hrtimer * hr)3211929185SAlexander Shishkin static enum hrtimer_restart stm_heartbeat_hrtimer_handler(struct hrtimer *hr)
3311929185SAlexander Shishkin {
3411929185SAlexander Shishkin 	struct stm_heartbeat *heartbeat = container_of(hr, struct stm_heartbeat,
3511929185SAlexander Shishkin 						       hrtimer);
3611929185SAlexander Shishkin 
3711929185SAlexander Shishkin 	stm_source_write(&heartbeat->data, 0, str, sizeof str);
3811929185SAlexander Shishkin 	if (heartbeat->active)
3911929185SAlexander Shishkin 		hrtimer_forward_now(hr, ms_to_ktime(interval_ms));
4011929185SAlexander Shishkin 
4111929185SAlexander Shishkin 	return heartbeat->active ? HRTIMER_RESTART : HRTIMER_NORESTART;
4211929185SAlexander Shishkin }
4311929185SAlexander Shishkin 
stm_heartbeat_link(struct stm_source_data * data)4411929185SAlexander Shishkin static int stm_heartbeat_link(struct stm_source_data *data)
4511929185SAlexander Shishkin {
4611929185SAlexander Shishkin 	struct stm_heartbeat *heartbeat =
4711929185SAlexander Shishkin 		container_of(data, struct stm_heartbeat, data);
4811929185SAlexander Shishkin 
4911929185SAlexander Shishkin 	heartbeat->active = 1;
5011929185SAlexander Shishkin 	hrtimer_start(&heartbeat->hrtimer, ms_to_ktime(interval_ms),
5111929185SAlexander Shishkin 		      HRTIMER_MODE_ABS);
5211929185SAlexander Shishkin 
5311929185SAlexander Shishkin 	return 0;
5411929185SAlexander Shishkin }
5511929185SAlexander Shishkin 
stm_heartbeat_unlink(struct stm_source_data * data)5611929185SAlexander Shishkin static void stm_heartbeat_unlink(struct stm_source_data *data)
5711929185SAlexander Shishkin {
5811929185SAlexander Shishkin 	struct stm_heartbeat *heartbeat =
5911929185SAlexander Shishkin 		container_of(data, struct stm_heartbeat, data);
6011929185SAlexander Shishkin 
6111929185SAlexander Shishkin 	heartbeat->active = 0;
6211929185SAlexander Shishkin 	hrtimer_cancel(&heartbeat->hrtimer);
6311929185SAlexander Shishkin }
6411929185SAlexander Shishkin 
stm_heartbeat_init(void)6511929185SAlexander Shishkin static int stm_heartbeat_init(void)
6611929185SAlexander Shishkin {
67*927633a6SWang Hui 	int i, ret;
6811929185SAlexander Shishkin 
69c8be4899SAlexander Shishkin 	if (nr_devs < 0 || nr_devs > STM_HEARTBEAT_MAX)
7011929185SAlexander Shishkin 		return -EINVAL;
7111929185SAlexander Shishkin 
72c8be4899SAlexander Shishkin 	for (i = 0; i < nr_devs; i++) {
7311929185SAlexander Shishkin 		stm_heartbeat[i].data.name =
7411929185SAlexander Shishkin 			kasprintf(GFP_KERNEL, "heartbeat.%d", i);
75*927633a6SWang Hui 		if (!stm_heartbeat[i].data.name) {
76*927633a6SWang Hui 			ret = -ENOMEM;
7711929185SAlexander Shishkin 			goto fail_unregister;
78*927633a6SWang Hui 		}
7911929185SAlexander Shishkin 
8011929185SAlexander Shishkin 		stm_heartbeat[i].data.nr_chans	= 1;
8111929185SAlexander Shishkin 		stm_heartbeat[i].data.link	= stm_heartbeat_link;
8211929185SAlexander Shishkin 		stm_heartbeat[i].data.unlink	= stm_heartbeat_unlink;
8311929185SAlexander Shishkin 		hrtimer_init(&stm_heartbeat[i].hrtimer, CLOCK_MONOTONIC,
8411929185SAlexander Shishkin 			     HRTIMER_MODE_ABS);
8511929185SAlexander Shishkin 		stm_heartbeat[i].hrtimer.function =
8611929185SAlexander Shishkin 			stm_heartbeat_hrtimer_handler;
8711929185SAlexander Shishkin 
8811929185SAlexander Shishkin 		ret = stm_source_register_device(NULL, &stm_heartbeat[i].data);
8911929185SAlexander Shishkin 		if (ret)
9011929185SAlexander Shishkin 			goto fail_free;
9111929185SAlexander Shishkin 	}
9211929185SAlexander Shishkin 
9311929185SAlexander Shishkin 	return 0;
9411929185SAlexander Shishkin 
9511929185SAlexander Shishkin fail_unregister:
9611929185SAlexander Shishkin 	for (i--; i >= 0; i--) {
9711929185SAlexander Shishkin 		stm_source_unregister_device(&stm_heartbeat[i].data);
9811929185SAlexander Shishkin fail_free:
9911929185SAlexander Shishkin 		kfree(stm_heartbeat[i].data.name);
10011929185SAlexander Shishkin 	}
10111929185SAlexander Shishkin 
10211929185SAlexander Shishkin 	return ret;
10311929185SAlexander Shishkin }
10411929185SAlexander Shishkin 
stm_heartbeat_exit(void)10511929185SAlexander Shishkin static void stm_heartbeat_exit(void)
10611929185SAlexander Shishkin {
10711929185SAlexander Shishkin 	int i;
10811929185SAlexander Shishkin 
109c8be4899SAlexander Shishkin 	for (i = 0; i < nr_devs; i++) {
11011929185SAlexander Shishkin 		stm_source_unregister_device(&stm_heartbeat[i].data);
11111929185SAlexander Shishkin 		kfree(stm_heartbeat[i].data.name);
11211929185SAlexander Shishkin 	}
11311929185SAlexander Shishkin }
11411929185SAlexander Shishkin 
11511929185SAlexander Shishkin module_init(stm_heartbeat_init);
11611929185SAlexander Shishkin module_exit(stm_heartbeat_exit);
11711929185SAlexander Shishkin 
11811929185SAlexander Shishkin MODULE_LICENSE("GPL v2");
11911929185SAlexander Shishkin MODULE_DESCRIPTION("stm_heartbeat driver");
12011929185SAlexander Shishkin MODULE_AUTHOR("Alexander Shishkin <alexander.shishkin@linux.intel.com>");
121