1 /* Copyright 2013-2018 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 <device.h>
18 #include <sbe-p8.h>
19 #include <skiboot.h>
20 #include <timebase.h>
21 #include <xscom.h>
22
23 /* SLW timer related stuff */
24 static bool sbe_has_timer;
25 static uint64_t sbe_timer_inc;
26 static uint64_t sbe_timer_target;
27 static uint32_t sbe_timer_chip;
28 static uint64_t sbe_last_gen;
29 static uint64_t sbe_last_gen_stamp;
30
p8_sbe_dump_timer_ffdc(void)31 static void p8_sbe_dump_timer_ffdc(void)
32 {
33 uint64_t i, val;
34 int64_t rc;
35
36 static const uint32_t dump_regs[] = {
37 0xe0000, 0xe0001, 0xe0002, 0xe0003,
38 0xe0004, 0xe0005, 0xe0006, 0xe0007,
39 0xe0008, 0xe0009, 0xe000a, 0xe000b,
40 0xe000c, 0xe000d, 0xe000e, 0xe000f,
41 0xe0010, 0xe0011, 0xe0012, 0xe0013,
42 0xe0014, 0xe0015, 0xe0016, 0xe0017,
43 0xe0018, 0xe0019,
44 0x5001c,
45 0x50038, 0x50039, 0x5003a, 0x5003b
46 };
47
48 /**
49 * @fwts-label SLWRegisterDump
50 * @fwts-advice An error condition occurred in sleep/winkle
51 * engines timer state machine. Dumping debug information to
52 * root-cause. OPAL/skiboot may be stuck on some operation that
53 * requires SLW timer state machine (e.g. core powersaving)
54 */
55 prlog(PR_DEBUG, "SLW: Register state:\n");
56
57 for (i = 0; i < ARRAY_SIZE(dump_regs); i++) {
58 uint32_t reg = dump_regs[i];
59 rc = xscom_read(sbe_timer_chip, reg, &val);
60 if (rc) {
61 prlog(PR_DEBUG, "SLW: XSCOM error %lld reading"
62 " reg 0x%x\n", rc, reg);
63 break;
64 }
65 prlog(PR_DEBUG, "SLW: %5x = %016llx\n", reg, val);
66 }
67 }
68
69 /* This is called with the timer lock held, so there is no
70 * issue with re-entrancy or concurrence
71 */
p8_sbe_update_timer_expiry(uint64_t new_target)72 void p8_sbe_update_timer_expiry(uint64_t new_target)
73 {
74 uint64_t count, gen, gen2, req, now;
75 int64_t rc;
76
77 if (!sbe_has_timer || new_target == sbe_timer_target)
78 return;
79
80 sbe_timer_target = new_target;
81
82 _xscom_lock();
83 now = mftb();
84 /* Calculate how many increments from now, rounded up */
85 if (now < new_target)
86 count = (new_target - now + sbe_timer_inc - 1) / sbe_timer_inc;
87 else
88 count = 1;
89
90 /* Max counter is 24-bit */
91 if (count > 0xffffff)
92 count = 0xffffff;
93 /* Fabricate update request */
94 req = (1ull << 63) | (count << 32);
95
96 prlog(PR_TRACE, "SLW: TMR expiry: 0x%llx, req: %016llx\n", count, req);
97
98 do {
99 /* Grab generation and spin if odd */
100 for (;;) {
101 rc = _xscom_read(sbe_timer_chip, 0xE0006, &gen, false);
102 if (rc) {
103 prerror("SLW: Error %lld reading tmr gen "
104 " count\n", rc);
105 _xscom_unlock();
106 return;
107 }
108 if (!(gen & 1))
109 break;
110 if (tb_compare(now + msecs_to_tb(1), mftb()) == TB_ABEFOREB) {
111 /**
112 * @fwts-label SLWTimerStuck
113 * @fwts-advice The SLeep/Winkle Engine (SLW)
114 * failed to increment the generation number
115 * within our timeout period (it *should* have
116 * done so within ~10us, not >1ms. OPAL uses
117 * the SLW timer to schedule some operations,
118 * but can fall back to the (much less frequent
119 * OPAL poller, which although does not affect
120 * functionality, runs *much* less frequently.
121 * This could have the effect of slow I2C
122 * operations (for example). It may also mean
123 * that you *had* an increase in jitter, due
124 * to slow interactions with SLW.
125 * This error may also occur if the machine
126 * is connected to via soft FSI.
127 */
128 prerror("SLW: timer stuck, falling back to OPAL pollers. You will likely have slower I2C and may have experienced increased jitter.\n");
129 prlog(PR_DEBUG, "SLW: Stuck with odd generation !\n");
130 _xscom_unlock();
131 sbe_has_timer = false;
132 p8_sbe_dump_timer_ffdc();
133 return;
134 }
135 }
136
137 rc = _xscom_write(sbe_timer_chip, 0x5003A, req, false);
138 if (rc) {
139 prerror("SLW: Error %lld writing tmr request\n", rc);
140 _xscom_unlock();
141 return;
142 }
143
144 /* Re-check gen count */
145 rc = _xscom_read(sbe_timer_chip, 0xE0006, &gen2, false);
146 if (rc) {
147 prerror("SLW: Error %lld re-reading tmr gen "
148 " count\n", rc);
149 _xscom_unlock();
150 return;
151 }
152 } while(gen != gen2);
153 _xscom_unlock();
154
155 /* Check if the timer is working. If at least 1ms has elapsed
156 * since the last call to this function, check that the gen
157 * count has changed
158 */
159 if (tb_compare(sbe_last_gen_stamp + msecs_to_tb(1), now)
160 == TB_ABEFOREB) {
161 if (sbe_last_gen == gen) {
162 prlog(PR_ERR,
163 "SLW: Timer appears to not be running !\n");
164 sbe_has_timer = false;
165 p8_sbe_dump_timer_ffdc();
166 }
167 sbe_last_gen = gen;
168 sbe_last_gen_stamp = mftb();
169 }
170
171 prlog(PR_TRACE, "SLW: gen: %llx\n", gen);
172 }
173
p8_sbe_timer_ok(void)174 bool p8_sbe_timer_ok(void)
175 {
176 return sbe_has_timer;
177 }
178
p8_sbe_init_timer(void)179 void p8_sbe_init_timer(void)
180 {
181 struct dt_node *np;
182 int64_t rc;
183 uint32_t tick_us;
184
185 np = dt_find_compatible_node(dt_root, NULL, "ibm,power8-sbe-timer");
186 if (!np)
187 return;
188
189 sbe_timer_chip = dt_get_chip_id(np);
190 tick_us = dt_prop_get_u32(np, "tick-time-us");
191 sbe_timer_inc = usecs_to_tb(tick_us);
192 sbe_timer_target = ~0ull;
193
194 rc = xscom_read(sbe_timer_chip, 0xE0006, &sbe_last_gen);
195 if (rc) {
196 prerror("SLW: Error %lld reading tmr gen count\n", rc);
197 return;
198 }
199 sbe_last_gen_stamp = mftb();
200
201 prlog(PR_INFO, "SLW: Timer facility on chip %d, resolution %dus\n",
202 sbe_timer_chip, tick_us);
203 sbe_has_timer = true;
204 }
205