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