Merge tag 'objtool-core-2020-08-03' of git://git.kernel.org/pub/scm/linux/kernel...
[linux-2.6-microblaze.git] / arch / x86 / platform / intel-mid / intel_mid_vrtc.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * intel_mid_vrtc.c: Driver for virtual RTC device on Intel MID platform
4  *
5  * (C) Copyright 2009 Intel Corporation
6  *
7  * Note:
8  * VRTC is emulated by system controller firmware, the real HW
9  * RTC is located in the PMIC device. SCU FW shadows PMIC RTC
10  * in a memory mapped IO space that is visible to the host IA
11  * processor.
12  *
13  * This driver is based on RTC CMOS driver.
14  */
15
16 #include <linux/kernel.h>
17 #include <linux/export.h>
18 #include <linux/init.h>
19 #include <linux/sfi.h>
20 #include <linux/platform_device.h>
21 #include <linux/mc146818rtc.h>
22
23 #include <asm/intel-mid.h>
24 #include <asm/intel_mid_vrtc.h>
25 #include <asm/time.h>
26 #include <asm/fixmap.h>
27
28 static unsigned char __iomem *vrtc_virt_base;
29
30 unsigned char vrtc_cmos_read(unsigned char reg)
31 {
32         unsigned char retval;
33
34         /* vRTC's registers range from 0x0 to 0xD */
35         if (reg > 0xd || !vrtc_virt_base)
36                 return 0xff;
37
38         lock_cmos_prefix(reg);
39         retval = __raw_readb(vrtc_virt_base + (reg << 2));
40         lock_cmos_suffix(reg);
41         return retval;
42 }
43 EXPORT_SYMBOL_GPL(vrtc_cmos_read);
44
45 void vrtc_cmos_write(unsigned char val, unsigned char reg)
46 {
47         if (reg > 0xd || !vrtc_virt_base)
48                 return;
49
50         lock_cmos_prefix(reg);
51         __raw_writeb(val, vrtc_virt_base + (reg << 2));
52         lock_cmos_suffix(reg);
53 }
54 EXPORT_SYMBOL_GPL(vrtc_cmos_write);
55
56 void vrtc_get_time(struct timespec64 *now)
57 {
58         u8 sec, min, hour, mday, mon;
59         unsigned long flags;
60         u32 year;
61
62         spin_lock_irqsave(&rtc_lock, flags);
63
64         while ((vrtc_cmos_read(RTC_FREQ_SELECT) & RTC_UIP))
65                 cpu_relax();
66
67         sec = vrtc_cmos_read(RTC_SECONDS);
68         min = vrtc_cmos_read(RTC_MINUTES);
69         hour = vrtc_cmos_read(RTC_HOURS);
70         mday = vrtc_cmos_read(RTC_DAY_OF_MONTH);
71         mon = vrtc_cmos_read(RTC_MONTH);
72         year = vrtc_cmos_read(RTC_YEAR);
73
74         spin_unlock_irqrestore(&rtc_lock, flags);
75
76         /* vRTC YEAR reg contains the offset to 1972 */
77         year += 1972;
78
79         pr_info("vRTC: sec: %d min: %d hour: %d day: %d "
80                 "mon: %d year: %d\n", sec, min, hour, mday, mon, year);
81
82         now->tv_sec = mktime64(year, mon, mday, hour, min, sec);
83         now->tv_nsec = 0;
84 }
85
86 int vrtc_set_mmss(const struct timespec64 *now)
87 {
88         unsigned long flags;
89         struct rtc_time tm;
90         int year;
91         int retval = 0;
92
93         rtc_time64_to_tm(now->tv_sec, &tm);
94         if (!rtc_valid_tm(&tm) && tm.tm_year >= 72) {
95                 /*
96                  * tm.year is the number of years since 1900, and the
97                  * vrtc need the years since 1972.
98                  */
99                 year = tm.tm_year - 72;
100                 spin_lock_irqsave(&rtc_lock, flags);
101                 vrtc_cmos_write(year, RTC_YEAR);
102                 vrtc_cmos_write(tm.tm_mon, RTC_MONTH);
103                 vrtc_cmos_write(tm.tm_mday, RTC_DAY_OF_MONTH);
104                 vrtc_cmos_write(tm.tm_hour, RTC_HOURS);
105                 vrtc_cmos_write(tm.tm_min, RTC_MINUTES);
106                 vrtc_cmos_write(tm.tm_sec, RTC_SECONDS);
107                 spin_unlock_irqrestore(&rtc_lock, flags);
108         } else {
109                 pr_err("%s: Invalid vRTC value: write of %llx to vRTC failed\n",
110                         __func__, (s64)now->tv_sec);
111                 retval = -EINVAL;
112         }
113         return retval;
114 }
115
116 void __init intel_mid_rtc_init(void)
117 {
118         unsigned long vrtc_paddr;
119
120         sfi_table_parse(SFI_SIG_MRTC, NULL, NULL, sfi_parse_mrtc);
121
122         vrtc_paddr = sfi_mrtc_array[0].phys_addr;
123         if (!sfi_mrtc_num || !vrtc_paddr)
124                 return;
125
126         vrtc_virt_base = (void __iomem *)set_fixmap_offset_nocache(FIX_LNW_VRTC,
127                                                                 vrtc_paddr);
128         x86_platform.get_wallclock = vrtc_get_time;
129         x86_platform.set_wallclock = vrtc_set_mmss;
130 }
131
132 /*
133  * The Moorestown platform has a memory mapped virtual RTC device that emulates
134  * the programming interface of the RTC.
135  */
136
137 static struct resource vrtc_resources[] = {
138         [0] = {
139                 .flags  = IORESOURCE_MEM,
140         },
141         [1] = {
142                 .flags  = IORESOURCE_IRQ,
143         }
144 };
145
146 static struct platform_device vrtc_device = {
147         .name           = "rtc_mrst",
148         .id             = -1,
149         .resource       = vrtc_resources,
150         .num_resources  = ARRAY_SIZE(vrtc_resources),
151 };
152
153 /* Register the RTC device if appropriate */
154 static int __init intel_mid_device_create(void)
155 {
156         /* No Moorestown, no device */
157         if (!intel_mid_identify_cpu())
158                 return -ENODEV;
159         /* No timer, no device */
160         if (!sfi_mrtc_num)
161                 return -ENODEV;
162
163         /* iomem resource */
164         vrtc_resources[0].start = sfi_mrtc_array[0].phys_addr;
165         vrtc_resources[0].end = sfi_mrtc_array[0].phys_addr +
166                                 MRST_VRTC_MAP_SZ;
167         /* irq resource */
168         vrtc_resources[1].start = sfi_mrtc_array[0].irq;
169         vrtc_resources[1].end = sfi_mrtc_array[0].irq;
170
171         return platform_device_register(&vrtc_device);
172 }
173 device_initcall(intel_mid_device_create);