18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2010, Microsoft Corporation. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Authors: 68c2ecf20Sopenharmony_ci * Haiyang Zhang <haiyangz@microsoft.com> 78c2ecf20Sopenharmony_ci * Hank Janssen <hjanssen@microsoft.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/init.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/slab.h> 158c2ecf20Sopenharmony_ci#include <linux/sysctl.h> 168c2ecf20Sopenharmony_ci#include <linux/reboot.h> 178c2ecf20Sopenharmony_ci#include <linux/hyperv.h> 188c2ecf20Sopenharmony_ci#include <linux/clockchips.h> 198c2ecf20Sopenharmony_ci#include <linux/ptp_clock_kernel.h> 208c2ecf20Sopenharmony_ci#include <clocksource/hyperv_timer.h> 218c2ecf20Sopenharmony_ci#include <asm/mshyperv.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include "hyperv_vmbus.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define SD_MAJOR 3 268c2ecf20Sopenharmony_ci#define SD_MINOR 0 278c2ecf20Sopenharmony_ci#define SD_MINOR_1 1 288c2ecf20Sopenharmony_ci#define SD_MINOR_2 2 298c2ecf20Sopenharmony_ci#define SD_VERSION_3_1 (SD_MAJOR << 16 | SD_MINOR_1) 308c2ecf20Sopenharmony_ci#define SD_VERSION_3_2 (SD_MAJOR << 16 | SD_MINOR_2) 318c2ecf20Sopenharmony_ci#define SD_VERSION (SD_MAJOR << 16 | SD_MINOR) 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#define SD_MAJOR_1 1 348c2ecf20Sopenharmony_ci#define SD_VERSION_1 (SD_MAJOR_1 << 16 | SD_MINOR) 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define TS_MAJOR 4 378c2ecf20Sopenharmony_ci#define TS_MINOR 0 388c2ecf20Sopenharmony_ci#define TS_VERSION (TS_MAJOR << 16 | TS_MINOR) 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#define TS_MAJOR_1 1 418c2ecf20Sopenharmony_ci#define TS_VERSION_1 (TS_MAJOR_1 << 16 | TS_MINOR) 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#define TS_MAJOR_3 3 448c2ecf20Sopenharmony_ci#define TS_VERSION_3 (TS_MAJOR_3 << 16 | TS_MINOR) 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci#define HB_MAJOR 3 478c2ecf20Sopenharmony_ci#define HB_MINOR 0 488c2ecf20Sopenharmony_ci#define HB_VERSION (HB_MAJOR << 16 | HB_MINOR) 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci#define HB_MAJOR_1 1 518c2ecf20Sopenharmony_ci#define HB_VERSION_1 (HB_MAJOR_1 << 16 | HB_MINOR) 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic int sd_srv_version; 548c2ecf20Sopenharmony_cistatic int ts_srv_version; 558c2ecf20Sopenharmony_cistatic int hb_srv_version; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci#define SD_VER_COUNT 4 588c2ecf20Sopenharmony_cistatic const int sd_versions[] = { 598c2ecf20Sopenharmony_ci SD_VERSION_3_2, 608c2ecf20Sopenharmony_ci SD_VERSION_3_1, 618c2ecf20Sopenharmony_ci SD_VERSION, 628c2ecf20Sopenharmony_ci SD_VERSION_1 638c2ecf20Sopenharmony_ci}; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci#define TS_VER_COUNT 3 668c2ecf20Sopenharmony_cistatic const int ts_versions[] = { 678c2ecf20Sopenharmony_ci TS_VERSION, 688c2ecf20Sopenharmony_ci TS_VERSION_3, 698c2ecf20Sopenharmony_ci TS_VERSION_1 708c2ecf20Sopenharmony_ci}; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci#define HB_VER_COUNT 2 738c2ecf20Sopenharmony_cistatic const int hb_versions[] = { 748c2ecf20Sopenharmony_ci HB_VERSION, 758c2ecf20Sopenharmony_ci HB_VERSION_1 768c2ecf20Sopenharmony_ci}; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci#define FW_VER_COUNT 2 798c2ecf20Sopenharmony_cistatic const int fw_versions[] = { 808c2ecf20Sopenharmony_ci UTIL_FW_VERSION, 818c2ecf20Sopenharmony_ci UTIL_WS2K8_FW_VERSION 828c2ecf20Sopenharmony_ci}; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci/* 858c2ecf20Sopenharmony_ci * Send the "hibernate" udev event in a thread context. 868c2ecf20Sopenharmony_ci */ 878c2ecf20Sopenharmony_cistruct hibernate_work_context { 888c2ecf20Sopenharmony_ci struct work_struct work; 898c2ecf20Sopenharmony_ci struct hv_device *dev; 908c2ecf20Sopenharmony_ci}; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic struct hibernate_work_context hibernate_context; 938c2ecf20Sopenharmony_cistatic bool hibernation_supported; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic void send_hibernate_uevent(struct work_struct *work) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci char *uevent_env[2] = { "EVENT=hibernate", NULL }; 988c2ecf20Sopenharmony_ci struct hibernate_work_context *ctx; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci ctx = container_of(work, struct hibernate_work_context, work); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci kobject_uevent_env(&ctx->dev->device.kobj, KOBJ_CHANGE, uevent_env); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci pr_info("Sent hibernation uevent\n"); 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic int hv_shutdown_init(struct hv_util_service *srv) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci struct vmbus_channel *channel = srv->channel; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci INIT_WORK(&hibernate_context.work, send_hibernate_uevent); 1128c2ecf20Sopenharmony_ci hibernate_context.dev = channel->device_obj; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci hibernation_supported = hv_is_hibernation_supported(); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci return 0; 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic void shutdown_onchannelcallback(void *context); 1208c2ecf20Sopenharmony_cistatic struct hv_util_service util_shutdown = { 1218c2ecf20Sopenharmony_ci .util_cb = shutdown_onchannelcallback, 1228c2ecf20Sopenharmony_ci .util_init = hv_shutdown_init, 1238c2ecf20Sopenharmony_ci}; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic int hv_timesync_init(struct hv_util_service *srv); 1268c2ecf20Sopenharmony_cistatic int hv_timesync_pre_suspend(void); 1278c2ecf20Sopenharmony_cistatic void hv_timesync_deinit(void); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic void timesync_onchannelcallback(void *context); 1308c2ecf20Sopenharmony_cistatic struct hv_util_service util_timesynch = { 1318c2ecf20Sopenharmony_ci .util_cb = timesync_onchannelcallback, 1328c2ecf20Sopenharmony_ci .util_init = hv_timesync_init, 1338c2ecf20Sopenharmony_ci .util_pre_suspend = hv_timesync_pre_suspend, 1348c2ecf20Sopenharmony_ci .util_deinit = hv_timesync_deinit, 1358c2ecf20Sopenharmony_ci}; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic void heartbeat_onchannelcallback(void *context); 1388c2ecf20Sopenharmony_cistatic struct hv_util_service util_heartbeat = { 1398c2ecf20Sopenharmony_ci .util_cb = heartbeat_onchannelcallback, 1408c2ecf20Sopenharmony_ci}; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic struct hv_util_service util_kvp = { 1438c2ecf20Sopenharmony_ci .util_cb = hv_kvp_onchannelcallback, 1448c2ecf20Sopenharmony_ci .util_init = hv_kvp_init, 1458c2ecf20Sopenharmony_ci .util_pre_suspend = hv_kvp_pre_suspend, 1468c2ecf20Sopenharmony_ci .util_pre_resume = hv_kvp_pre_resume, 1478c2ecf20Sopenharmony_ci .util_deinit = hv_kvp_deinit, 1488c2ecf20Sopenharmony_ci}; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic struct hv_util_service util_vss = { 1518c2ecf20Sopenharmony_ci .util_cb = hv_vss_onchannelcallback, 1528c2ecf20Sopenharmony_ci .util_init = hv_vss_init, 1538c2ecf20Sopenharmony_ci .util_pre_suspend = hv_vss_pre_suspend, 1548c2ecf20Sopenharmony_ci .util_pre_resume = hv_vss_pre_resume, 1558c2ecf20Sopenharmony_ci .util_deinit = hv_vss_deinit, 1568c2ecf20Sopenharmony_ci}; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic struct hv_util_service util_fcopy = { 1598c2ecf20Sopenharmony_ci .util_cb = hv_fcopy_onchannelcallback, 1608c2ecf20Sopenharmony_ci .util_init = hv_fcopy_init, 1618c2ecf20Sopenharmony_ci .util_pre_suspend = hv_fcopy_pre_suspend, 1628c2ecf20Sopenharmony_ci .util_pre_resume = hv_fcopy_pre_resume, 1638c2ecf20Sopenharmony_ci .util_deinit = hv_fcopy_deinit, 1648c2ecf20Sopenharmony_ci}; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic void perform_shutdown(struct work_struct *dummy) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci orderly_poweroff(true); 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_cistatic void perform_restart(struct work_struct *dummy) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci orderly_reboot(); 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci/* 1778c2ecf20Sopenharmony_ci * Perform the shutdown operation in a thread context. 1788c2ecf20Sopenharmony_ci */ 1798c2ecf20Sopenharmony_cistatic DECLARE_WORK(shutdown_work, perform_shutdown); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci/* 1828c2ecf20Sopenharmony_ci * Perform the restart operation in a thread context. 1838c2ecf20Sopenharmony_ci */ 1848c2ecf20Sopenharmony_cistatic DECLARE_WORK(restart_work, perform_restart); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic void shutdown_onchannelcallback(void *context) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci struct vmbus_channel *channel = context; 1898c2ecf20Sopenharmony_ci struct work_struct *work = NULL; 1908c2ecf20Sopenharmony_ci u32 recvlen; 1918c2ecf20Sopenharmony_ci u64 requestid; 1928c2ecf20Sopenharmony_ci u8 *shut_txf_buf = util_shutdown.recv_buffer; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci struct shutdown_msg_data *shutdown_msg; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci struct icmsg_hdr *icmsghdrp; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci vmbus_recvpacket(channel, shut_txf_buf, 1998c2ecf20Sopenharmony_ci HV_HYP_PAGE_SIZE, &recvlen, &requestid); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci if (recvlen > 0) { 2028c2ecf20Sopenharmony_ci icmsghdrp = (struct icmsg_hdr *)&shut_txf_buf[ 2038c2ecf20Sopenharmony_ci sizeof(struct vmbuspipe_hdr)]; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { 2068c2ecf20Sopenharmony_ci if (vmbus_prep_negotiate_resp(icmsghdrp, shut_txf_buf, 2078c2ecf20Sopenharmony_ci fw_versions, FW_VER_COUNT, 2088c2ecf20Sopenharmony_ci sd_versions, SD_VER_COUNT, 2098c2ecf20Sopenharmony_ci NULL, &sd_srv_version)) { 2108c2ecf20Sopenharmony_ci pr_info("Shutdown IC version %d.%d\n", 2118c2ecf20Sopenharmony_ci sd_srv_version >> 16, 2128c2ecf20Sopenharmony_ci sd_srv_version & 0xFFFF); 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci } else { 2158c2ecf20Sopenharmony_ci shutdown_msg = 2168c2ecf20Sopenharmony_ci (struct shutdown_msg_data *)&shut_txf_buf[ 2178c2ecf20Sopenharmony_ci sizeof(struct vmbuspipe_hdr) + 2188c2ecf20Sopenharmony_ci sizeof(struct icmsg_hdr)]; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci /* 2218c2ecf20Sopenharmony_ci * shutdown_msg->flags can be 0(shut down), 2(reboot), 2228c2ecf20Sopenharmony_ci * or 4(hibernate). It may bitwise-OR 1, which means 2238c2ecf20Sopenharmony_ci * performing the request by force. Linux always tries 2248c2ecf20Sopenharmony_ci * to perform the request by force. 2258c2ecf20Sopenharmony_ci */ 2268c2ecf20Sopenharmony_ci switch (shutdown_msg->flags) { 2278c2ecf20Sopenharmony_ci case 0: 2288c2ecf20Sopenharmony_ci case 1: 2298c2ecf20Sopenharmony_ci icmsghdrp->status = HV_S_OK; 2308c2ecf20Sopenharmony_ci work = &shutdown_work; 2318c2ecf20Sopenharmony_ci pr_info("Shutdown request received -" 2328c2ecf20Sopenharmony_ci " graceful shutdown initiated\n"); 2338c2ecf20Sopenharmony_ci break; 2348c2ecf20Sopenharmony_ci case 2: 2358c2ecf20Sopenharmony_ci case 3: 2368c2ecf20Sopenharmony_ci icmsghdrp->status = HV_S_OK; 2378c2ecf20Sopenharmony_ci work = &restart_work; 2388c2ecf20Sopenharmony_ci pr_info("Restart request received -" 2398c2ecf20Sopenharmony_ci " graceful restart initiated\n"); 2408c2ecf20Sopenharmony_ci break; 2418c2ecf20Sopenharmony_ci case 4: 2428c2ecf20Sopenharmony_ci case 5: 2438c2ecf20Sopenharmony_ci pr_info("Hibernation request received\n"); 2448c2ecf20Sopenharmony_ci icmsghdrp->status = hibernation_supported ? 2458c2ecf20Sopenharmony_ci HV_S_OK : HV_E_FAIL; 2468c2ecf20Sopenharmony_ci if (hibernation_supported) 2478c2ecf20Sopenharmony_ci work = &hibernate_context.work; 2488c2ecf20Sopenharmony_ci break; 2498c2ecf20Sopenharmony_ci default: 2508c2ecf20Sopenharmony_ci icmsghdrp->status = HV_E_FAIL; 2518c2ecf20Sopenharmony_ci pr_info("Shutdown request received -" 2528c2ecf20Sopenharmony_ci " Invalid request\n"); 2538c2ecf20Sopenharmony_ci break; 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION 2588c2ecf20Sopenharmony_ci | ICMSGHDRFLAG_RESPONSE; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci vmbus_sendpacket(channel, shut_txf_buf, 2618c2ecf20Sopenharmony_ci recvlen, requestid, 2628c2ecf20Sopenharmony_ci VM_PKT_DATA_INBAND, 0); 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci if (work) 2668c2ecf20Sopenharmony_ci schedule_work(work); 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci/* 2708c2ecf20Sopenharmony_ci * Set the host time in a process context. 2718c2ecf20Sopenharmony_ci */ 2728c2ecf20Sopenharmony_cistatic struct work_struct adj_time_work; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci/* 2758c2ecf20Sopenharmony_ci * The last time sample, received from the host. PTP device responds to 2768c2ecf20Sopenharmony_ci * requests by using this data and the current partition-wide time reference 2778c2ecf20Sopenharmony_ci * count. 2788c2ecf20Sopenharmony_ci */ 2798c2ecf20Sopenharmony_cistatic struct { 2808c2ecf20Sopenharmony_ci u64 host_time; 2818c2ecf20Sopenharmony_ci u64 ref_time; 2828c2ecf20Sopenharmony_ci spinlock_t lock; 2838c2ecf20Sopenharmony_ci} host_ts; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_cistatic inline u64 reftime_to_ns(u64 reftime) 2868c2ecf20Sopenharmony_ci{ 2878c2ecf20Sopenharmony_ci return (reftime - WLTIMEDELTA) * 100; 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci/* 2918c2ecf20Sopenharmony_ci * Hard coded threshold for host timesync delay: 600 seconds 2928c2ecf20Sopenharmony_ci */ 2938c2ecf20Sopenharmony_cistatic const u64 HOST_TIMESYNC_DELAY_THRESH = 600 * (u64)NSEC_PER_SEC; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_cistatic int hv_get_adj_host_time(struct timespec64 *ts) 2968c2ecf20Sopenharmony_ci{ 2978c2ecf20Sopenharmony_ci u64 newtime, reftime, timediff_adj; 2988c2ecf20Sopenharmony_ci unsigned long flags; 2998c2ecf20Sopenharmony_ci int ret = 0; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci spin_lock_irqsave(&host_ts.lock, flags); 3028c2ecf20Sopenharmony_ci reftime = hv_read_reference_counter(); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci /* 3058c2ecf20Sopenharmony_ci * We need to let the caller know that last update from host 3068c2ecf20Sopenharmony_ci * is older than the max allowable threshold. clock_gettime() 3078c2ecf20Sopenharmony_ci * and PTP ioctl do not have a documented error that we could 3088c2ecf20Sopenharmony_ci * return for this specific case. Use ESTALE to report this. 3098c2ecf20Sopenharmony_ci */ 3108c2ecf20Sopenharmony_ci timediff_adj = reftime - host_ts.ref_time; 3118c2ecf20Sopenharmony_ci if (timediff_adj * 100 > HOST_TIMESYNC_DELAY_THRESH) { 3128c2ecf20Sopenharmony_ci pr_warn_once("TIMESYNC IC: Stale time stamp, %llu nsecs old\n", 3138c2ecf20Sopenharmony_ci (timediff_adj * 100)); 3148c2ecf20Sopenharmony_ci ret = -ESTALE; 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci newtime = host_ts.host_time + timediff_adj; 3188c2ecf20Sopenharmony_ci *ts = ns_to_timespec64(reftime_to_ns(newtime)); 3198c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&host_ts.lock, flags); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci return ret; 3228c2ecf20Sopenharmony_ci} 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_cistatic void hv_set_host_time(struct work_struct *work) 3258c2ecf20Sopenharmony_ci{ 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci struct timespec64 ts; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci if (!hv_get_adj_host_time(&ts)) 3308c2ecf20Sopenharmony_ci do_settimeofday64(&ts); 3318c2ecf20Sopenharmony_ci} 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci/* 3348c2ecf20Sopenharmony_ci * Synchronize time with host after reboot, restore, etc. 3358c2ecf20Sopenharmony_ci * 3368c2ecf20Sopenharmony_ci * ICTIMESYNCFLAG_SYNC flag bit indicates reboot, restore events of the VM. 3378c2ecf20Sopenharmony_ci * After reboot the flag ICTIMESYNCFLAG_SYNC is included in the first time 3388c2ecf20Sopenharmony_ci * message after the timesync channel is opened. Since the hv_utils module is 3398c2ecf20Sopenharmony_ci * loaded after hv_vmbus, the first message is usually missed. This bit is 3408c2ecf20Sopenharmony_ci * considered a hard request to discipline the clock. 3418c2ecf20Sopenharmony_ci * 3428c2ecf20Sopenharmony_ci * ICTIMESYNCFLAG_SAMPLE bit indicates a time sample from host. This is 3438c2ecf20Sopenharmony_ci * typically used as a hint to the guest. The guest is under no obligation 3448c2ecf20Sopenharmony_ci * to discipline the clock. 3458c2ecf20Sopenharmony_ci */ 3468c2ecf20Sopenharmony_cistatic inline void adj_guesttime(u64 hosttime, u64 reftime, u8 adj_flags) 3478c2ecf20Sopenharmony_ci{ 3488c2ecf20Sopenharmony_ci unsigned long flags; 3498c2ecf20Sopenharmony_ci u64 cur_reftime; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci /* 3528c2ecf20Sopenharmony_ci * Save the adjusted time sample from the host and the snapshot 3538c2ecf20Sopenharmony_ci * of the current system time. 3548c2ecf20Sopenharmony_ci */ 3558c2ecf20Sopenharmony_ci spin_lock_irqsave(&host_ts.lock, flags); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci cur_reftime = hv_read_reference_counter(); 3588c2ecf20Sopenharmony_ci host_ts.host_time = hosttime; 3598c2ecf20Sopenharmony_ci host_ts.ref_time = cur_reftime; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci /* 3628c2ecf20Sopenharmony_ci * TimeSync v4 messages contain reference time (guest's Hyper-V 3638c2ecf20Sopenharmony_ci * clocksource read when the time sample was generated), we can 3648c2ecf20Sopenharmony_ci * improve the precision by adding the delta between now and the 3658c2ecf20Sopenharmony_ci * time of generation. For older protocols we set 3668c2ecf20Sopenharmony_ci * reftime == cur_reftime on call. 3678c2ecf20Sopenharmony_ci */ 3688c2ecf20Sopenharmony_ci host_ts.host_time += (cur_reftime - reftime); 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&host_ts.lock, flags); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci /* Schedule work to do do_settimeofday64() */ 3738c2ecf20Sopenharmony_ci if (adj_flags & ICTIMESYNCFLAG_SYNC) 3748c2ecf20Sopenharmony_ci schedule_work(&adj_time_work); 3758c2ecf20Sopenharmony_ci} 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci/* 3788c2ecf20Sopenharmony_ci * Time Sync Channel message handler. 3798c2ecf20Sopenharmony_ci */ 3808c2ecf20Sopenharmony_cistatic void timesync_onchannelcallback(void *context) 3818c2ecf20Sopenharmony_ci{ 3828c2ecf20Sopenharmony_ci struct vmbus_channel *channel = context; 3838c2ecf20Sopenharmony_ci u32 recvlen; 3848c2ecf20Sopenharmony_ci u64 requestid; 3858c2ecf20Sopenharmony_ci struct icmsg_hdr *icmsghdrp; 3868c2ecf20Sopenharmony_ci struct ictimesync_data *timedatap; 3878c2ecf20Sopenharmony_ci struct ictimesync_ref_data *refdata; 3888c2ecf20Sopenharmony_ci u8 *time_txf_buf = util_timesynch.recv_buffer; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci /* 3918c2ecf20Sopenharmony_ci * Drain the ring buffer and use the last packet to update 3928c2ecf20Sopenharmony_ci * host_ts 3938c2ecf20Sopenharmony_ci */ 3948c2ecf20Sopenharmony_ci while (1) { 3958c2ecf20Sopenharmony_ci int ret = vmbus_recvpacket(channel, time_txf_buf, 3968c2ecf20Sopenharmony_ci HV_HYP_PAGE_SIZE, &recvlen, 3978c2ecf20Sopenharmony_ci &requestid); 3988c2ecf20Sopenharmony_ci if (ret) { 3998c2ecf20Sopenharmony_ci pr_warn_once("TimeSync IC pkt recv failed (Err: %d)\n", 4008c2ecf20Sopenharmony_ci ret); 4018c2ecf20Sopenharmony_ci break; 4028c2ecf20Sopenharmony_ci } 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci if (!recvlen) 4058c2ecf20Sopenharmony_ci break; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci icmsghdrp = (struct icmsg_hdr *)&time_txf_buf[ 4088c2ecf20Sopenharmony_ci sizeof(struct vmbuspipe_hdr)]; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { 4118c2ecf20Sopenharmony_ci if (vmbus_prep_negotiate_resp(icmsghdrp, time_txf_buf, 4128c2ecf20Sopenharmony_ci fw_versions, FW_VER_COUNT, 4138c2ecf20Sopenharmony_ci ts_versions, TS_VER_COUNT, 4148c2ecf20Sopenharmony_ci NULL, &ts_srv_version)) { 4158c2ecf20Sopenharmony_ci pr_info("TimeSync IC version %d.%d\n", 4168c2ecf20Sopenharmony_ci ts_srv_version >> 16, 4178c2ecf20Sopenharmony_ci ts_srv_version & 0xFFFF); 4188c2ecf20Sopenharmony_ci } 4198c2ecf20Sopenharmony_ci } else { 4208c2ecf20Sopenharmony_ci if (ts_srv_version > TS_VERSION_3) { 4218c2ecf20Sopenharmony_ci refdata = (struct ictimesync_ref_data *) 4228c2ecf20Sopenharmony_ci &time_txf_buf[ 4238c2ecf20Sopenharmony_ci sizeof(struct vmbuspipe_hdr) + 4248c2ecf20Sopenharmony_ci sizeof(struct icmsg_hdr)]; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci adj_guesttime(refdata->parenttime, 4278c2ecf20Sopenharmony_ci refdata->vmreferencetime, 4288c2ecf20Sopenharmony_ci refdata->flags); 4298c2ecf20Sopenharmony_ci } else { 4308c2ecf20Sopenharmony_ci timedatap = (struct ictimesync_data *) 4318c2ecf20Sopenharmony_ci &time_txf_buf[ 4328c2ecf20Sopenharmony_ci sizeof(struct vmbuspipe_hdr) + 4338c2ecf20Sopenharmony_ci sizeof(struct icmsg_hdr)]; 4348c2ecf20Sopenharmony_ci adj_guesttime(timedatap->parenttime, 4358c2ecf20Sopenharmony_ci hv_read_reference_counter(), 4368c2ecf20Sopenharmony_ci timedatap->flags); 4378c2ecf20Sopenharmony_ci } 4388c2ecf20Sopenharmony_ci } 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION 4418c2ecf20Sopenharmony_ci | ICMSGHDRFLAG_RESPONSE; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci vmbus_sendpacket(channel, time_txf_buf, 4448c2ecf20Sopenharmony_ci recvlen, requestid, 4458c2ecf20Sopenharmony_ci VM_PKT_DATA_INBAND, 0); 4468c2ecf20Sopenharmony_ci } 4478c2ecf20Sopenharmony_ci} 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci/* 4508c2ecf20Sopenharmony_ci * Heartbeat functionality. 4518c2ecf20Sopenharmony_ci * Every two seconds, Hyper-V send us a heartbeat request message. 4528c2ecf20Sopenharmony_ci * we respond to this message, and Hyper-V knows we are alive. 4538c2ecf20Sopenharmony_ci */ 4548c2ecf20Sopenharmony_cistatic void heartbeat_onchannelcallback(void *context) 4558c2ecf20Sopenharmony_ci{ 4568c2ecf20Sopenharmony_ci struct vmbus_channel *channel = context; 4578c2ecf20Sopenharmony_ci u32 recvlen; 4588c2ecf20Sopenharmony_ci u64 requestid; 4598c2ecf20Sopenharmony_ci struct icmsg_hdr *icmsghdrp; 4608c2ecf20Sopenharmony_ci struct heartbeat_msg_data *heartbeat_msg; 4618c2ecf20Sopenharmony_ci u8 *hbeat_txf_buf = util_heartbeat.recv_buffer; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci while (1) { 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci vmbus_recvpacket(channel, hbeat_txf_buf, 4668c2ecf20Sopenharmony_ci HV_HYP_PAGE_SIZE, &recvlen, &requestid); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci if (!recvlen) 4698c2ecf20Sopenharmony_ci break; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci icmsghdrp = (struct icmsg_hdr *)&hbeat_txf_buf[ 4728c2ecf20Sopenharmony_ci sizeof(struct vmbuspipe_hdr)]; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { 4758c2ecf20Sopenharmony_ci if (vmbus_prep_negotiate_resp(icmsghdrp, 4768c2ecf20Sopenharmony_ci hbeat_txf_buf, 4778c2ecf20Sopenharmony_ci fw_versions, FW_VER_COUNT, 4788c2ecf20Sopenharmony_ci hb_versions, HB_VER_COUNT, 4798c2ecf20Sopenharmony_ci NULL, &hb_srv_version)) { 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci pr_info("Heartbeat IC version %d.%d\n", 4828c2ecf20Sopenharmony_ci hb_srv_version >> 16, 4838c2ecf20Sopenharmony_ci hb_srv_version & 0xFFFF); 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci } else { 4868c2ecf20Sopenharmony_ci heartbeat_msg = 4878c2ecf20Sopenharmony_ci (struct heartbeat_msg_data *)&hbeat_txf_buf[ 4888c2ecf20Sopenharmony_ci sizeof(struct vmbuspipe_hdr) + 4898c2ecf20Sopenharmony_ci sizeof(struct icmsg_hdr)]; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci heartbeat_msg->seq_num += 1; 4928c2ecf20Sopenharmony_ci } 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION 4958c2ecf20Sopenharmony_ci | ICMSGHDRFLAG_RESPONSE; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci vmbus_sendpacket(channel, hbeat_txf_buf, 4988c2ecf20Sopenharmony_ci recvlen, requestid, 4998c2ecf20Sopenharmony_ci VM_PKT_DATA_INBAND, 0); 5008c2ecf20Sopenharmony_ci } 5018c2ecf20Sopenharmony_ci} 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci#define HV_UTIL_RING_SEND_SIZE VMBUS_RING_SIZE(3 * HV_HYP_PAGE_SIZE) 5048c2ecf20Sopenharmony_ci#define HV_UTIL_RING_RECV_SIZE VMBUS_RING_SIZE(3 * HV_HYP_PAGE_SIZE) 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_cistatic int util_probe(struct hv_device *dev, 5078c2ecf20Sopenharmony_ci const struct hv_vmbus_device_id *dev_id) 5088c2ecf20Sopenharmony_ci{ 5098c2ecf20Sopenharmony_ci struct hv_util_service *srv = 5108c2ecf20Sopenharmony_ci (struct hv_util_service *)dev_id->driver_data; 5118c2ecf20Sopenharmony_ci int ret; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci srv->recv_buffer = kmalloc(HV_HYP_PAGE_SIZE * 4, GFP_KERNEL); 5148c2ecf20Sopenharmony_ci if (!srv->recv_buffer) 5158c2ecf20Sopenharmony_ci return -ENOMEM; 5168c2ecf20Sopenharmony_ci srv->channel = dev->channel; 5178c2ecf20Sopenharmony_ci if (srv->util_init) { 5188c2ecf20Sopenharmony_ci ret = srv->util_init(srv); 5198c2ecf20Sopenharmony_ci if (ret) { 5208c2ecf20Sopenharmony_ci ret = -ENODEV; 5218c2ecf20Sopenharmony_ci goto error1; 5228c2ecf20Sopenharmony_ci } 5238c2ecf20Sopenharmony_ci } 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci /* 5268c2ecf20Sopenharmony_ci * The set of services managed by the util driver are not performance 5278c2ecf20Sopenharmony_ci * critical and do not need batched reading. Furthermore, some services 5288c2ecf20Sopenharmony_ci * such as KVP can only handle one message from the host at a time. 5298c2ecf20Sopenharmony_ci * Turn off batched reading for all util drivers before we open the 5308c2ecf20Sopenharmony_ci * channel. 5318c2ecf20Sopenharmony_ci */ 5328c2ecf20Sopenharmony_ci set_channel_read_mode(dev->channel, HV_CALL_DIRECT); 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci hv_set_drvdata(dev, srv); 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci ret = vmbus_open(dev->channel, HV_UTIL_RING_SEND_SIZE, 5378c2ecf20Sopenharmony_ci HV_UTIL_RING_RECV_SIZE, NULL, 0, srv->util_cb, 5388c2ecf20Sopenharmony_ci dev->channel); 5398c2ecf20Sopenharmony_ci if (ret) 5408c2ecf20Sopenharmony_ci goto error; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci return 0; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_cierror: 5458c2ecf20Sopenharmony_ci if (srv->util_deinit) 5468c2ecf20Sopenharmony_ci srv->util_deinit(); 5478c2ecf20Sopenharmony_cierror1: 5488c2ecf20Sopenharmony_ci kfree(srv->recv_buffer); 5498c2ecf20Sopenharmony_ci return ret; 5508c2ecf20Sopenharmony_ci} 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_cistatic int util_remove(struct hv_device *dev) 5538c2ecf20Sopenharmony_ci{ 5548c2ecf20Sopenharmony_ci struct hv_util_service *srv = hv_get_drvdata(dev); 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci if (srv->util_deinit) 5578c2ecf20Sopenharmony_ci srv->util_deinit(); 5588c2ecf20Sopenharmony_ci vmbus_close(dev->channel); 5598c2ecf20Sopenharmony_ci kfree(srv->recv_buffer); 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci return 0; 5628c2ecf20Sopenharmony_ci} 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci/* 5658c2ecf20Sopenharmony_ci * When we're in util_suspend(), all the userspace processes have been frozen 5668c2ecf20Sopenharmony_ci * (refer to hibernate() -> freeze_processes()). The userspace is thawed only 5678c2ecf20Sopenharmony_ci * after the whole resume procedure, including util_resume(), finishes. 5688c2ecf20Sopenharmony_ci */ 5698c2ecf20Sopenharmony_cistatic int util_suspend(struct hv_device *dev) 5708c2ecf20Sopenharmony_ci{ 5718c2ecf20Sopenharmony_ci struct hv_util_service *srv = hv_get_drvdata(dev); 5728c2ecf20Sopenharmony_ci int ret = 0; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci if (srv->util_pre_suspend) { 5758c2ecf20Sopenharmony_ci ret = srv->util_pre_suspend(); 5768c2ecf20Sopenharmony_ci if (ret) 5778c2ecf20Sopenharmony_ci return ret; 5788c2ecf20Sopenharmony_ci } 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci vmbus_close(dev->channel); 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci return 0; 5838c2ecf20Sopenharmony_ci} 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_cistatic int util_resume(struct hv_device *dev) 5868c2ecf20Sopenharmony_ci{ 5878c2ecf20Sopenharmony_ci struct hv_util_service *srv = hv_get_drvdata(dev); 5888c2ecf20Sopenharmony_ci int ret = 0; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci if (srv->util_pre_resume) { 5918c2ecf20Sopenharmony_ci ret = srv->util_pre_resume(); 5928c2ecf20Sopenharmony_ci if (ret) 5938c2ecf20Sopenharmony_ci return ret; 5948c2ecf20Sopenharmony_ci } 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci ret = vmbus_open(dev->channel, HV_UTIL_RING_SEND_SIZE, 5978c2ecf20Sopenharmony_ci HV_UTIL_RING_RECV_SIZE, NULL, 0, srv->util_cb, 5988c2ecf20Sopenharmony_ci dev->channel); 5998c2ecf20Sopenharmony_ci return ret; 6008c2ecf20Sopenharmony_ci} 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_cistatic const struct hv_vmbus_device_id id_table[] = { 6038c2ecf20Sopenharmony_ci /* Shutdown guid */ 6048c2ecf20Sopenharmony_ci { HV_SHUTDOWN_GUID, 6058c2ecf20Sopenharmony_ci .driver_data = (unsigned long)&util_shutdown 6068c2ecf20Sopenharmony_ci }, 6078c2ecf20Sopenharmony_ci /* Time synch guid */ 6088c2ecf20Sopenharmony_ci { HV_TS_GUID, 6098c2ecf20Sopenharmony_ci .driver_data = (unsigned long)&util_timesynch 6108c2ecf20Sopenharmony_ci }, 6118c2ecf20Sopenharmony_ci /* Heartbeat guid */ 6128c2ecf20Sopenharmony_ci { HV_HEART_BEAT_GUID, 6138c2ecf20Sopenharmony_ci .driver_data = (unsigned long)&util_heartbeat 6148c2ecf20Sopenharmony_ci }, 6158c2ecf20Sopenharmony_ci /* KVP guid */ 6168c2ecf20Sopenharmony_ci { HV_KVP_GUID, 6178c2ecf20Sopenharmony_ci .driver_data = (unsigned long)&util_kvp 6188c2ecf20Sopenharmony_ci }, 6198c2ecf20Sopenharmony_ci /* VSS GUID */ 6208c2ecf20Sopenharmony_ci { HV_VSS_GUID, 6218c2ecf20Sopenharmony_ci .driver_data = (unsigned long)&util_vss 6228c2ecf20Sopenharmony_ci }, 6238c2ecf20Sopenharmony_ci /* File copy GUID */ 6248c2ecf20Sopenharmony_ci { HV_FCOPY_GUID, 6258c2ecf20Sopenharmony_ci .driver_data = (unsigned long)&util_fcopy 6268c2ecf20Sopenharmony_ci }, 6278c2ecf20Sopenharmony_ci { }, 6288c2ecf20Sopenharmony_ci}; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(vmbus, id_table); 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci/* The one and only one */ 6338c2ecf20Sopenharmony_cistatic struct hv_driver util_drv = { 6348c2ecf20Sopenharmony_ci .name = "hv_utils", 6358c2ecf20Sopenharmony_ci .id_table = id_table, 6368c2ecf20Sopenharmony_ci .probe = util_probe, 6378c2ecf20Sopenharmony_ci .remove = util_remove, 6388c2ecf20Sopenharmony_ci .suspend = util_suspend, 6398c2ecf20Sopenharmony_ci .resume = util_resume, 6408c2ecf20Sopenharmony_ci .driver = { 6418c2ecf20Sopenharmony_ci .probe_type = PROBE_PREFER_ASYNCHRONOUS, 6428c2ecf20Sopenharmony_ci }, 6438c2ecf20Sopenharmony_ci}; 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_cistatic int hv_ptp_enable(struct ptp_clock_info *info, 6468c2ecf20Sopenharmony_ci struct ptp_clock_request *request, int on) 6478c2ecf20Sopenharmony_ci{ 6488c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 6498c2ecf20Sopenharmony_ci} 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_cistatic int hv_ptp_settime(struct ptp_clock_info *p, const struct timespec64 *ts) 6528c2ecf20Sopenharmony_ci{ 6538c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 6548c2ecf20Sopenharmony_ci} 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_cistatic int hv_ptp_adjfreq(struct ptp_clock_info *ptp, s32 delta) 6578c2ecf20Sopenharmony_ci{ 6588c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 6598c2ecf20Sopenharmony_ci} 6608c2ecf20Sopenharmony_cistatic int hv_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) 6618c2ecf20Sopenharmony_ci{ 6628c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 6638c2ecf20Sopenharmony_ci} 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_cistatic int hv_ptp_gettime(struct ptp_clock_info *info, struct timespec64 *ts) 6668c2ecf20Sopenharmony_ci{ 6678c2ecf20Sopenharmony_ci return hv_get_adj_host_time(ts); 6688c2ecf20Sopenharmony_ci} 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_cistatic struct ptp_clock_info ptp_hyperv_info = { 6718c2ecf20Sopenharmony_ci .name = "hyperv", 6728c2ecf20Sopenharmony_ci .enable = hv_ptp_enable, 6738c2ecf20Sopenharmony_ci .adjtime = hv_ptp_adjtime, 6748c2ecf20Sopenharmony_ci .adjfreq = hv_ptp_adjfreq, 6758c2ecf20Sopenharmony_ci .gettime64 = hv_ptp_gettime, 6768c2ecf20Sopenharmony_ci .settime64 = hv_ptp_settime, 6778c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 6788c2ecf20Sopenharmony_ci}; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_cistatic struct ptp_clock *hv_ptp_clock; 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_cistatic int hv_timesync_init(struct hv_util_service *srv) 6838c2ecf20Sopenharmony_ci{ 6848c2ecf20Sopenharmony_ci /* TimeSync requires Hyper-V clocksource. */ 6858c2ecf20Sopenharmony_ci if (!hv_read_reference_counter) 6868c2ecf20Sopenharmony_ci return -ENODEV; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci spin_lock_init(&host_ts.lock); 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci INIT_WORK(&adj_time_work, hv_set_host_time); 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci /* 6938c2ecf20Sopenharmony_ci * ptp_clock_register() returns NULL when CONFIG_PTP_1588_CLOCK is 6948c2ecf20Sopenharmony_ci * disabled but the driver is still useful without the PTP device 6958c2ecf20Sopenharmony_ci * as it still handles the ICTIMESYNCFLAG_SYNC case. 6968c2ecf20Sopenharmony_ci */ 6978c2ecf20Sopenharmony_ci hv_ptp_clock = ptp_clock_register(&ptp_hyperv_info, NULL); 6988c2ecf20Sopenharmony_ci if (IS_ERR_OR_NULL(hv_ptp_clock)) { 6998c2ecf20Sopenharmony_ci pr_err("cannot register PTP clock: %d\n", 7008c2ecf20Sopenharmony_ci PTR_ERR_OR_ZERO(hv_ptp_clock)); 7018c2ecf20Sopenharmony_ci hv_ptp_clock = NULL; 7028c2ecf20Sopenharmony_ci } 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci return 0; 7058c2ecf20Sopenharmony_ci} 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_cistatic void hv_timesync_cancel_work(void) 7088c2ecf20Sopenharmony_ci{ 7098c2ecf20Sopenharmony_ci cancel_work_sync(&adj_time_work); 7108c2ecf20Sopenharmony_ci} 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_cistatic int hv_timesync_pre_suspend(void) 7138c2ecf20Sopenharmony_ci{ 7148c2ecf20Sopenharmony_ci hv_timesync_cancel_work(); 7158c2ecf20Sopenharmony_ci return 0; 7168c2ecf20Sopenharmony_ci} 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_cistatic void hv_timesync_deinit(void) 7198c2ecf20Sopenharmony_ci{ 7208c2ecf20Sopenharmony_ci if (hv_ptp_clock) 7218c2ecf20Sopenharmony_ci ptp_clock_unregister(hv_ptp_clock); 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci hv_timesync_cancel_work(); 7248c2ecf20Sopenharmony_ci} 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_cistatic int __init init_hyperv_utils(void) 7278c2ecf20Sopenharmony_ci{ 7288c2ecf20Sopenharmony_ci pr_info("Registering HyperV Utility Driver\n"); 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci return vmbus_driver_register(&util_drv); 7318c2ecf20Sopenharmony_ci} 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_cistatic void exit_hyperv_utils(void) 7348c2ecf20Sopenharmony_ci{ 7358c2ecf20Sopenharmony_ci pr_info("De-Registered HyperV Utility Driver\n"); 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci vmbus_driver_unregister(&util_drv); 7388c2ecf20Sopenharmony_ci} 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_cimodule_init(init_hyperv_utils); 7418c2ecf20Sopenharmony_cimodule_exit(exit_hyperv_utils); 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Hyper-V Utilities"); 7448c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 745