~pmdj/ubuntu/trusty/qemu/2.9+applesmc+fadtv3

« back to all changes in this revision

Viewing changes to roms/skiboot/hw/lpc-rtc.c

  • Committer: Phil Dennis-Jordan
  • Date: 2017-07-21 08:03:43 UTC
  • mfrom: (1.1.1)
  • Revision ID: phil@philjordan.eu-20170721080343-2yr2vdj7713czahv
New upstream release 2.9.0.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright 2015 IBM Corp.
 
2
 *
 
3
 * Licensed under the Apache License, Version 2.0 (the "License");
 
4
 * you may not use this file except in compliance with the License.
 
5
 * You may obtain a copy of the License at
 
6
 *
 
7
 *      http://www.apache.org/licenses/LICENSE-2.0
 
8
 *
 
9
 * Unless required by applicable law or agreed to in writing, software
 
10
 * distributed under the License is distributed on an "AS IS" BASIS,
 
11
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 
12
 * implied.
 
13
 * See the License for the specific language governing permissions and
 
14
 * limitations under the License.
 
15
 */
 
16
 
 
17
#include <stdlib.h>
 
18
#include <string.h>
 
19
#include <ipmi.h>
 
20
#include <time.h>
 
21
#include <time-utils.h>
 
22
#include <device.h>
 
23
#include <opal.h>
 
24
#include <rtc.h>
 
25
#include <lpc.h>
 
26
#include <lock.h>
 
27
#include <timebase.h>
 
28
 
 
29
/* Legacy RTC registers */
 
30
#define RTC_REG_SECONDS         0
 
31
#define RTC_REG_MINUTES         2
 
32
#define RTC_REG_HOURS           4
 
33
#define RTC_REG_DAY_OF_WEEK     6
 
34
#define RTC_REG_DAY_OF_MONTH    7
 
35
#define RTC_REG_MONTH           8
 
36
#define RTC_REG_YEAR            9
 
37
#define RTC_REG_A               10
 
38
#define   RTC_REG_A_UIP                 0x80
 
39
#define RTC_REG_B               11
 
40
#define   RTC_REG_B_DIS_UPD             0x80
 
41
#define   RTC_REG_B_PIE                 0x40
 
42
#define   RTC_REG_B_AIE                 0x20
 
43
#define   RTC_REG_B_UIE                 0x10
 
44
#define   RTC_REG_B_SQWE                0x08
 
45
#define   RTC_REG_B_DM_BINARY           0x04
 
46
#define   RTC_REG_B_24H                 0x02
 
47
#define   RTC_REG_B_DST_EN              0x01
 
48
#define RTC_REG_C               12
 
49
#define RTC_REG_D               13
 
50
#define   RTC_REG_D_VALID               0x80
 
51
 
 
52
/* Init value is no interrupts, 24H mode, updates enabled */
 
53
#define RTC_REG_B_INIT          (RTC_REG_B_24H)
 
54
 
 
55
static u32 rtc_port;
 
56
static struct lock rtc_lock = LOCK_UNLOCKED;
 
57
 
 
58
static uint8_t rtc_read(uint8_t reg)
 
59
{
 
60
        lpc_outb(reg, rtc_port);
 
61
        return lpc_inb(rtc_port + 1);
 
62
}
 
63
 
 
64
static void rtc_write(uint8_t reg, uint8_t val)
 
65
{
 
66
        lpc_outb(reg, rtc_port);
 
67
        lpc_outb(val, rtc_port + 1);
 
68
}
 
69
 
 
70
static bool lpc_rtc_read_tm(struct tm *tm)
 
71
{
 
72
        struct tm tm2;
 
73
        unsigned int loops = 0;
 
74
 
 
75
        /* Read until two series provide identical values, this
 
76
         * should deal with update races in all practical cases
 
77
         */
 
78
        for (;;) {
 
79
                tm2 = *tm;
 
80
                tm->tm_sec = rtc_read(RTC_REG_SECONDS);
 
81
                tm->tm_min = rtc_read(RTC_REG_MINUTES);
 
82
                tm->tm_hour = rtc_read(RTC_REG_HOURS);
 
83
                tm->tm_mday = rtc_read(RTC_REG_DAY_OF_MONTH);
 
84
                tm->tm_mon = rtc_read(RTC_REG_MONTH);
 
85
                tm->tm_year = rtc_read(RTC_REG_YEAR);
 
86
                if (loops > 0 && memcmp(&tm2, tm, sizeof(struct tm)) == 0)
 
87
                        break;
 
88
                loops++;
 
89
                if (loops > 10) {
 
90
                        prerror("RTC: Failed to obtain stable values\n");
 
91
                        return false;
 
92
                }
 
93
        }
 
94
        tm->tm_sec = bcd_byte(tm->tm_sec, 0);
 
95
        tm->tm_min = bcd_byte(tm->tm_min, 0);
 
96
        tm->tm_hour = bcd_byte(tm->tm_hour, 0);
 
97
        tm->tm_mday = bcd_byte(tm->tm_mday, 0);
 
98
        tm->tm_mon = bcd_byte(tm->tm_mon, 0) - 1;
 
99
        tm->tm_year = bcd_byte(tm->tm_year, 0);
 
100
 
 
101
        /* 2000 wrap */
 
102
        if (tm->tm_year < 69)
 
103
                tm->tm_year += 100;
 
104
 
 
105
        /* Base */
 
106
        tm->tm_year += 1900;
 
107
 
 
108
        return true;
 
109
}
 
110
 
 
111
static void lpc_rtc_write_tm(struct tm *tm __unused)
 
112
{
 
113
        /* XXX */
 
114
}
 
115
 
 
116
static void lpc_init_time(void)
 
117
{
 
118
        uint8_t val;
 
119
        struct tm tm;
 
120
        bool valid;
 
121
 
 
122
        memset(&tm, 0, sizeof(tm));
 
123
 
 
124
        lock(&rtc_lock);
 
125
 
 
126
        /* If update is in progress, wait a bit */
 
127
        val = rtc_read(RTC_REG_A);
 
128
        if (val & RTC_REG_A_UIP)
 
129
                time_wait_ms(10);
 
130
 
 
131
        /* Read from RTC */
 
132
        valid = lpc_rtc_read_tm(&tm);
 
133
 
 
134
        unlock(&rtc_lock);
 
135
 
 
136
        /* Update cache */
 
137
        if (valid)
 
138
                rtc_cache_update(&tm);
 
139
}
 
140
 
 
141
static void lpc_init_hw(void)
 
142
{
 
143
        lock(&rtc_lock);
 
144
 
 
145
        /* Set REG B to a suitable default */
 
146
        rtc_write(RTC_REG_B, RTC_REG_B_INIT);
 
147
 
 
148
        unlock(&rtc_lock);
 
149
}
 
150
 
 
151
static int64_t lpc_opal_rtc_read(uint32_t *y_m_d,
 
152
                                 uint64_t *h_m_s_m)
 
153
{
 
154
        uint8_t val;
 
155
        int64_t rc = OPAL_SUCCESS;
 
156
        struct tm tm;
 
157
 
 
158
        if (!y_m_d || !h_m_s_m)
 
159
                return OPAL_PARAMETER;
 
160
 
 
161
        /* Return busy if updating. This is somewhat racy, but will
 
162
         * do for now, most RTCs nowadays are smart enough to atomically
 
163
         * update. Alternatively we could just read from the cache...
 
164
         */
 
165
        lock(&rtc_lock);
 
166
        val = rtc_read(RTC_REG_A);
 
167
        if (val & RTC_REG_A_UIP) {
 
168
                unlock(&rtc_lock);
 
169
                return OPAL_BUSY_EVENT;
 
170
        }
 
171
 
 
172
        /* Read from RTC */
 
173
        if (lpc_rtc_read_tm(&tm))
 
174
                rc = OPAL_SUCCESS;
 
175
        else
 
176
                rc = OPAL_HARDWARE;
 
177
        unlock(&rtc_lock);
 
178
 
 
179
        if (rc == OPAL_SUCCESS) {
 
180
                /* Update cache */
 
181
                rtc_cache_update(&tm);
 
182
 
 
183
                /* Convert to OPAL time */
 
184
                tm_to_datetime(&tm, y_m_d, h_m_s_m);
 
185
        }
 
186
 
 
187
        return rc;
 
188
}
 
189
 
 
190
static int64_t lpc_opal_rtc_write(uint32_t year_month_day,
 
191
                                  uint64_t hour_minute_second_millisecond)
 
192
{
 
193
        struct tm tm;
 
194
 
 
195
        /* Convert to struct tm */
 
196
        datetime_to_tm(year_month_day, hour_minute_second_millisecond, &tm);
 
197
 
 
198
        /* Write it out */
 
199
        lock(&rtc_lock);
 
200
        lpc_rtc_write_tm(&tm);
 
201
        unlock(&rtc_lock);
 
202
 
 
203
        return OPAL_SUCCESS;
 
204
}
 
205
 
 
206
void lpc_rtc_init(void)
 
207
{
 
208
        struct dt_node *rtc_node, *np;
 
209
 
 
210
        if (!lpc_present())
 
211
                return;
 
212
 
 
213
        /* We support only one */
 
214
        rtc_node = dt_find_compatible_node(dt_root, NULL, "pnpPNP,b00");
 
215
        if (!rtc_node)
 
216
                return;
 
217
 
 
218
        /* Get IO base */
 
219
        rtc_port = dt_prop_get_cell_def(rtc_node, "reg", 1, 0);
 
220
        if (!rtc_port) {
 
221
                prerror("RTC: Can't find reg property\n");
 
222
                return;
 
223
        }
 
224
        if (dt_prop_get_cell_def(rtc_node, "reg", 0, 0) != OPAL_LPC_IO) {
 
225
                prerror("RTC: Unsupported address type\n");
 
226
                return;
 
227
        }
 
228
 
 
229
        /* Init the HW */
 
230
        lpc_init_hw();
 
231
 
 
232
        /* Create OPAL API node and register OPAL calls */
 
233
        np = dt_new(opal_node, "rtc");
 
234
        dt_add_property_strings(np, "compatible", "ibm,opal-rtc");
 
235
 
 
236
        opal_register(OPAL_RTC_READ, lpc_opal_rtc_read, 2);
 
237
        opal_register(OPAL_RTC_WRITE, lpc_opal_rtc_write, 2);
 
238
 
 
239
        /* Initialise the rtc cache */
 
240
        lpc_init_time();
 
241
}