aboutsummaryrefslogtreecommitdiff
path: root/hw/slw.c
diff options
context:
space:
mode:
authorBenjamin Herrenschmidt <benh@au1.ibm.com>2015-09-10 10:09:26 +1000
committerStewart Smith <stewart@linux.vnet.ibm.com>2015-09-11 16:42:17 +1000
commit9af5751a00faf9ad3fe4b52f56de9bd7835d9bb7 (patch)
tree5be21b54d2b9d301b9983cf18554c1386cdce00d /hw/slw.c
parentb5f05ac337b3495cf506f5492fd053e90ae0bda3 (diff)
downloadskiboot-9af5751a00faf9ad3fe4b52f56de9bd7835d9bb7.zip
skiboot-9af5751a00faf9ad3fe4b52f56de9bd7835d9bb7.tar.gz
skiboot-9af5751a00faf9ad3fe4b52f56de9bd7835d9bb7.tar.bz2
slw/timer: SBE based timer support
Recent HostBoot & SBE firmware provide a HW timer facility that can be used to implement OPAL timers and thus limit the reliance on the Linux heartbeat. This implements support for it. The side effect is that i2c from Centaurs is now usable. Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org> [stewart@linux.vnet.ibm.com: fix run-timer unit test] Signed-off-by: Stewart Smith <stewart@linux.vnet.ibm.com>
Diffstat (limited to 'hw/slw.c')
-rw-r--r--hw/slw.c183
1 files changed, 168 insertions, 15 deletions
diff --git a/hw/slw.c b/hw/slw.c
index 9aec41c..a009090 100644
--- a/hw/slw.c
+++ b/hw/slw.c
@@ -40,6 +40,14 @@ static uint32_t slw_saved_reset[MAX_RESET_PATCH_SIZE];
static bool slw_current_le = false;
#endif /* __HAVE_LIBPORE__ */
+/* SLW timer related stuff */
+static bool slw_has_timer;
+static uint64_t slw_timer_inc;
+static uint64_t slw_timer_target;
+static uint32_t slw_timer_chip;
+static uint64_t slw_last_gen;
+static uint64_t slw_last_gen_stamp;
+
/* Assembly in head.S */
extern void enter_rvwinkle(void);
@@ -897,7 +905,7 @@ static void slw_patch_regs(struct proc_chip *chip)
static void slw_init_chip(struct proc_chip *chip)
{
- int rc __unused;
+ int64_t rc;
struct cpu_thread *c;
prlog(PR_DEBUG, "SLW: Init chip 0x%x\n", chip->id);
@@ -913,7 +921,7 @@ static void slw_init_chip(struct proc_chip *chip)
&chip->slw_image_size);
if (rc != 0) {
log_simple_error(&e_info(OPAL_RC_SLW_INIT),
- "SLW: Error %d reading SLW image size\n", rc);
+ "SLW: Error %lld reading SLW image size\n", rc);
/* XXX Panic ? */
chip->slw_base = 0;
chip->slw_bar_size = 0;
@@ -939,19 +947,6 @@ static void slw_init_chip(struct proc_chip *chip)
}
}
-void slw_init(void)
-{
- struct proc_chip *chip;
-
- if (proc_gen != proc_gen_p8)
- return;
-
- for_each_chip(chip)
- slw_init_chip(chip);
-
- add_cpu_idle_state_properties();
-}
-
/* Workarounds while entering fast-sleep */
static void fast_sleep_enter(void)
@@ -1085,3 +1080,161 @@ static int64_t opal_slw_set_reg(uint64_t cpu_pir, uint64_t sprn, uint64_t val)
opal_call(OPAL_SLW_SET_REG, opal_slw_set_reg, 3);
#endif /* __HAVE_LIBPORE__ */
+
+static void slw_dump_timer_ffdc(void)
+{
+ uint64_t i, val;
+ int64_t rc;
+
+ static const uint32_t dump_regs[] = {
+ 0xe0000, 0xe0001, 0xe0002, 0xe0003,
+ 0xe0004, 0xe0005, 0xe0006, 0xe0007,
+ 0xe0008, 0xe0009, 0xe000a, 0xe000b,
+ 0xe000c, 0xe000d, 0xe000e, 0xe000f,
+ 0xe0010, 0xe0011, 0xe0012, 0xe0013,
+ 0xe0014, 0xe0015, 0xe0016, 0xe0017,
+ 0xe0018, 0xe0019,
+ 0x5001c,
+ 0x50038, 0x50039, 0x5003a, 0x5003b
+ };
+
+ prlog(PR_ERR, "SLW: Register state:\n");
+
+ for (i = 0; i < ARRAY_SIZE(dump_regs); i++) {
+ uint32_t reg = dump_regs[i];
+ rc = xscom_read(slw_timer_chip, reg, &val);
+ if (rc) {
+ prlog(PR_ERR, "SLW: XSCOM error %lld reading"
+ " reg 0x%x\n", rc, reg);
+ break;
+ }
+ prlog(PR_ERR, "SLW: %5x = %016llx\n", reg, val);
+ }
+}
+
+/* This is called with the timer lock held, so there is no
+ * issue with re-entrancy or concurrence
+ */
+void slw_update_timer_expiry(uint64_t new_target)
+{
+ uint64_t count, gen, gen2, req, now = mftb();
+ int64_t rc;
+
+ if (!slw_has_timer || new_target == slw_timer_target)
+ return;
+
+ slw_timer_target = new_target;
+
+ /* Calculate how many increments from now, rounded up */
+ if (now < new_target)
+ count = (new_target - now + slw_timer_inc - 1) / slw_timer_inc;
+ else
+ count = 1;
+
+ /* Max counter is 24-bit */
+ if (count > 0xffffff)
+ count = 0xffffff;
+ /* Fabricate update request */
+ req = (1ull << 63) | (count << 32);
+
+ prlog(PR_TRACE, "SLW: TMR expiry: 0x%llx, req: %016llx\n", count, req);
+
+ do {
+ /* Grab generation and spin if odd */
+ for (;;) {
+ rc = xscom_read(slw_timer_chip, 0xE0006, &gen);
+ if (rc) {
+ prerror("SLW: Error %lld reading tmr gen "
+ " count\n", rc);
+ return;
+ }
+ if (!(gen & 1))
+ break;
+ if (tb_compare(now + msecs_to_tb(1), mftb()) == TB_ABEFOREB) {
+ prerror("SLW: Stuck with odd generation !\n");
+ slw_has_timer = false;
+ slw_dump_timer_ffdc();
+ return;
+ }
+ }
+
+ rc = xscom_write(slw_timer_chip, 0x5003A, req);
+ if (rc) {
+ prerror("SLW: Error %lld writing tmr request\n", rc);
+ return;
+ }
+
+ /* Re-check gen count */
+ rc = xscom_read(slw_timer_chip, 0xE0006, &gen2);
+ if (rc) {
+ prerror("SLW: Error %lld re-reading tmr gen "
+ " count\n", rc);
+ return;
+ }
+ } while(gen != gen2);
+
+ /* Check if the timer is working. If at least 1ms has elapsed
+ * since the last call to this function, check that the gen
+ * count has changed
+ */
+ if (tb_compare(slw_last_gen_stamp + msecs_to_tb(1), now)
+ == TB_ABEFOREB) {
+ if (slw_last_gen == gen) {
+ prlog(PR_ERR,
+ "SLW: Timer appears to not be running !\n");
+ slw_has_timer = false;
+ slw_dump_timer_ffdc();
+ }
+ slw_last_gen = gen;
+ slw_last_gen_stamp = mftb();
+ }
+
+ prlog(PR_TRACE, "SLW: gen: %llx\n", gen);
+}
+
+bool slw_timer_ok(void)
+{
+ return slw_has_timer;
+}
+
+static void slw_init_timer(void)
+{
+ struct dt_node *np;
+ int64_t rc;
+ uint32_t tick_us;
+
+ np = dt_find_compatible_node(dt_root, NULL, "ibm,power8-sbe-timer");
+ if (!np)
+ return;
+
+ slw_timer_chip = dt_get_chip_id(np);
+ tick_us = dt_prop_get_u32(np, "tick-time-us");
+ slw_timer_inc = usecs_to_tb(tick_us);
+ slw_timer_target = ~0ull;
+
+ rc = xscom_read(slw_timer_chip, 0xE0006, &slw_last_gen);
+ if (rc) {
+ prerror("SLW: Error %lld reading tmr gen count\n", rc);
+ return;
+ }
+ slw_last_gen_stamp = mftb();
+
+ prlog(PR_INFO, "SLW: Timer facility on chip %d, resolution %dus\n",
+ slw_timer_chip, tick_us);
+ slw_has_timer = true;
+}
+
+void slw_init(void)
+{
+ struct proc_chip *chip;
+
+ if (proc_gen != proc_gen_p8)
+ return;
+
+ for_each_chip(chip)
+ slw_init_chip(chip);
+
+ add_cpu_idle_state_properties();
+
+ slw_init_timer();
+}