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