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