| 1 | |
| 2 | |
| 3 | * linux/arch/m68k/kernel/time.c
|
| 4 | *
|
| 5 | * Copyright (C) 1991, 1992, 1995 Linus Torvalds
|
| 6 | *
|
| 7 | * This file contains the m68k-specific time handling details.
|
| 8 | * Most of the stuff is located in the machine specific files.
|
| 9 | *
|
| 10 | * 1997-09-10 Updated NTP code according to technical memorandum Jan '96
|
| 11 | * "A Kernel Model for Precision Timekeeping" by Dave Mills
|
| 12 |
|
| 13 |
|
| 14 | #include <linux/errno.h>
|
| 15 | #include <linux/export.h>
|
| 16 | #include <linux/module.h>
|
| 17 | #include <linux/sched.h>
|
| 18 | #include <linux/sched/loadavg.h>
|
| 19 | #include <linux/kernel.h>
|
| 20 | #include <linux/param.h>
|
| 21 | #include <linux/string.h>
|
| 22 | #include <linux/mm.h>
|
| 23 | #include <linux/rtc.h>
|
| 24 | #include <linux/platform_device.h>
|
| 25 |
|
| 26 | #include <asm/machdep.h>
|
| 27 | #include <asm/io.h>
|
| 28 | #include <asm/irq_regs.h>
|
| 29 |
|
| 30 | #include <linux/time.h>
|
| 31 | #include <linux/timex.h>
|
| 32 | #include <linux/profile.h>
|
| 33 |
|
| 34 |
|
| 35 | unsigned long (*mach_random_get_entropy)(void);
|
| 36 | EXPORT_SYMBOL_GPL(mach_random_get_entropy);
|
| 37 |
|
| 38 | #ifdef CONFIG_HEARTBEAT
|
| 39 | void timer_heartbeat(void)
|
| 40 | {
|
| 41 | |
| 42 | for debugging -- based on the version for PReP by Cort
|
| 43 |
|
| 44 | if (mach_heartbeat) {
|
| 45 | static unsigned cnt = 0, period = 0, dist = 0;
|
| 46 |
|
| 47 | if (cnt == 0 || cnt == dist)
|
| 48 | mach_heartbeat( 1 );
|
| 49 | else if (cnt == 7 || cnt == dist+7)
|
| 50 | mach_heartbeat( 0 );
|
| 51 |
|
| 52 | if (++cnt > period) {
|
| 53 | cnt = 0;
|
| 54 | |
| 55 | * length in dependency of the current (5min) load. It goes
|
| 56 | * through the points f(0)=126, f(1)=86, f(5)=51,
|
| 57 | * f(inf)->30.
|
| 58 | period = ((672<<FSHIFT)/(5*avenrun[0]+(7<<FSHIFT))) + 30;
|
| 59 | dist = period / 4;
|
| 60 | }
|
| 61 | }
|
| 62 | }
|
| 63 | #endif
|
| 64 |
|
| 65 | #if defined(CONFIG_M68KCLASSIC) || defined(CONFIG_SUN3)
|
| 66 |
|
| 67 | int (*mach_hwclk) (int, struct rtc_time*);
|
| 68 | EXPORT_SYMBOL(mach_hwclk);
|
| 69 |
|
| 70 | int (*mach_get_rtc_pll)(struct rtc_pll_info *);
|
| 71 | int (*mach_set_rtc_pll)(struct rtc_pll_info *);
|
| 72 | EXPORT_SYMBOL(mach_get_rtc_pll);
|
| 73 | EXPORT_SYMBOL(mach_set_rtc_pll);
|
| 74 |
|
| 75 | #if !IS_BUILTIN(CONFIG_RTC_DRV_GENERIC)
|
| 76 | void read_persistent_clock64(struct timespec64 *ts)
|
| 77 | {
|
| 78 | struct rtc_time time;
|
| 79 |
|
| 80 | ts->tv_sec = 0;
|
| 81 | ts->tv_nsec = 0;
|
| 82 |
|
| 83 | if (!mach_hwclk)
|
| 84 | return;
|
| 85 |
|
| 86 | mach_hwclk(0, &time);
|
| 87 |
|
| 88 | ts->tv_sec = mktime64(time.tm_year + 1900, time.tm_mon + 1, time.tm_mday,
|
| 89 | time.tm_hour, time.tm_min, time.tm_sec);
|
| 90 | }
|
| 91 | #endif
|
| 92 |
|
| 93 | #if IS_ENABLED(CONFIG_RTC_DRV_GENERIC)
|
| 94 | static int rtc_generic_get_time(struct device *dev, struct rtc_time *tm)
|
| 95 | {
|
| 96 | mach_hwclk(0, tm);
|
| 97 | return 0;
|
| 98 | }
|
| 99 |
|
| 100 | static int rtc_generic_set_time(struct device *dev, struct rtc_time *tm)
|
| 101 | {
|
| 102 | if (mach_hwclk(1, tm) < 0)
|
| 103 | return -EOPNOTSUPP;
|
| 104 | return 0;
|
| 105 | }
|
| 106 |
|
| 107 | static int rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
|
| 108 | {
|
| 109 | struct rtc_pll_info pll;
|
| 110 | struct rtc_pll_info __user *argp = (void __user *)arg;
|
| 111 |
|
| 112 | switch (cmd) {
|
| 113 | case RTC_PLL_GET:
|
| 114 | if (!mach_get_rtc_pll || mach_get_rtc_pll(&pll))
|
| 115 | return -EINVAL;
|
| 116 | return copy_to_user(argp, &pll, sizeof pll) ? -EFAULT : 0;
|
| 117 |
|
| 118 | case RTC_PLL_SET:
|
| 119 | if (!mach_set_rtc_pll)
|
| 120 | return -EINVAL;
|
| 121 | if (!capable(CAP_SYS_TIME))
|
| 122 | return -EACCES;
|
| 123 | if (copy_from_user(&pll, argp, sizeof(pll)))
|
| 124 | return -EFAULT;
|
| 125 | return mach_set_rtc_pll(&pll);
|
| 126 | }
|
| 127 |
|
| 128 | return -ENOIOCTLCMD;
|
| 129 | }
|
| 130 |
|
| 131 | static const struct rtc_class_ops generic_rtc_ops = {
|
| 132 | .ioctl = rtc_ioctl,
|
| 133 | .read_time = rtc_generic_get_time,
|
| 134 | .set_time = rtc_generic_set_time,
|
| 135 | };
|
| 136 |
|
| 137 | static int __init rtc_init(void)
|
| 138 | {
|
| 139 | struct platform_device *pdev;
|
| 140 |
|
| 141 | if (!mach_hwclk)
|
| 142 | return -ENODEV;
|
| 143 |
|
| 144 | pdev = platform_device_register_data(NULL, "rtc-generic", -1,
|
| 145 | &generic_rtc_ops,
|
| 146 | sizeof(generic_rtc_ops));
|
| 147 | return PTR_ERR_OR_ZERO(pdev);
|
| 148 | }
|
| 149 |
|
| 150 | module_init(rtc_init);
|
| 151 | #endif
|
| 152 | #endif
|
| 153 |
|
| 154 | void __init time_init(void)
|
| 155 | {
|
| 156 | mach_sched_init();
|
| 157 | }
|