Commit 628daa8d5abfd904a7329a660c5c374212230123

Authored by Benjamin Herrenschmidt
1 parent ec27329ffb

powerpc/powernv: Add RTC and NVRAM support plus RTAS fallbacks

Implements OPAL RTC and NVRAM support and wire all that up to
the powernv platform.

We use RTAS for RTC as a fallback if available. Using RTAS for nvram
is not supported yet, pending some rework/cleanup and generalization
of the pSeries & CHRP code. We also use RTAS fallbacks for power off
and reboot

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>

Showing 5 changed files with 229 additions and 21 deletions Side-by-side Diff

arch/powerpc/include/asm/opal.h
... ... @@ -430,6 +430,12 @@
430 430  
431 431 extern void hvc_opal_init_early(void);
432 432  
  433 +struct rtc_time;
  434 +extern int opal_set_rtc_time(struct rtc_time *tm);
  435 +extern void opal_get_rtc_time(struct rtc_time *tm);
  436 +extern unsigned long opal_get_boot_time(void);
  437 +extern void opal_nvram_init(void);
  438 +
433 439 #endif /* __ASSEMBLY__ */
434 440  
435 441 #endif /* __OPAL_H */
arch/powerpc/platforms/powernv/Makefile
1 1 obj-y += setup.o opal-takeover.o opal-wrappers.o opal.o
  2 +obj-y += opal-rtc.o opal-nvram.o
  3 +
2 4 obj-$(CONFIG_SMP) += smp.o
arch/powerpc/platforms/powernv/opal-nvram.c
  1 +/*
  2 + * PowerNV nvram code.
  3 + *
  4 + * Copyright 2011 IBM Corp.
  5 + *
  6 + * This program is free software; you can redistribute it and/or
  7 + * modify it under the terms of the GNU General Public License
  8 + * as published by the Free Software Foundation; either version
  9 + * 2 of the License, or (at your option) any later version.
  10 + */
  11 +
  12 +#define DEBUG
  13 +
  14 +#include <linux/kernel.h>
  15 +#include <linux/init.h>
  16 +#include <linux/of.h>
  17 +
  18 +#include <asm/opal.h>
  19 +#include <asm/machdep.h>
  20 +
  21 +static unsigned int nvram_size;
  22 +
  23 +static ssize_t opal_nvram_size(void)
  24 +{
  25 + return nvram_size;
  26 +}
  27 +
  28 +static ssize_t opal_nvram_read(char *buf, size_t count, loff_t *index)
  29 +{
  30 + s64 rc;
  31 + int off;
  32 +
  33 + if (*index >= nvram_size)
  34 + return 0;
  35 + off = *index;
  36 + if ((off + count) > nvram_size)
  37 + count = nvram_size - off;
  38 + rc = opal_read_nvram(__pa(buf), count, off);
  39 + if (rc != OPAL_SUCCESS)
  40 + return -EIO;
  41 + *index += count;
  42 + return count;
  43 +}
  44 +
  45 +static ssize_t opal_nvram_write(char *buf, size_t count, loff_t *index)
  46 +{
  47 + s64 rc = OPAL_BUSY;
  48 + int off;
  49 +
  50 + if (*index >= nvram_size)
  51 + return 0;
  52 + off = *index;
  53 + if ((off + count) > nvram_size)
  54 + count = nvram_size - off;
  55 +
  56 + while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
  57 + rc = opal_write_nvram(__pa(buf), count, off);
  58 + if (rc == OPAL_BUSY_EVENT)
  59 + opal_poll_events(NULL);
  60 + }
  61 + *index += count;
  62 + return count;
  63 +}
  64 +
  65 +void __init opal_nvram_init(void)
  66 +{
  67 + struct device_node *np;
  68 + const u32 *nbytes_p;
  69 +
  70 + np = of_find_compatible_node(NULL, NULL, "ibm,opal-nvram");
  71 + if (np == NULL)
  72 + return;
  73 +
  74 + nbytes_p = of_get_property(np, "#bytes", NULL);
  75 + if (!nbytes_p) {
  76 + of_node_put(np);
  77 + return;
  78 + }
  79 + nvram_size = *nbytes_p;
  80 +
  81 + printk(KERN_INFO "OPAL nvram setup, %u bytes\n", nvram_size);
  82 + of_node_put(np);
  83 +
  84 + ppc_md.nvram_read = opal_nvram_read;
  85 + ppc_md.nvram_write = opal_nvram_write;
  86 + ppc_md.nvram_size = opal_nvram_size;
  87 +}
arch/powerpc/platforms/powernv/opal-rtc.c
  1 +/*
  2 + * PowerNV Real Time Clock.
  3 + *
  4 + * Copyright 2011 IBM Corp.
  5 + *
  6 + * This program is free software; you can redistribute it and/or
  7 + * modify it under the terms of the GNU General Public License
  8 + * as published by the Free Software Foundation; either version
  9 + * 2 of the License, or (at your option) any later version.
  10 + */
  11 +
  12 +
  13 +#include <linux/kernel.h>
  14 +#include <linux/time.h>
  15 +#include <linux/bcd.h>
  16 +#include <linux/rtc.h>
  17 +#include <linux/delay.h>
  18 +
  19 +#include <asm/opal.h>
  20 +#include <asm/firmware.h>
  21 +
  22 +static void opal_to_tm(u32 y_m_d, u64 h_m_s_ms, struct rtc_time *tm)
  23 +{
  24 + tm->tm_year = ((bcd2bin(y_m_d >> 24) * 100) +
  25 + bcd2bin((y_m_d >> 16) & 0xff)) - 1900;
  26 + tm->tm_mon = bcd2bin((y_m_d >> 8) & 0xff) - 1;
  27 + tm->tm_mday = bcd2bin(y_m_d & 0xff);
  28 + tm->tm_hour = bcd2bin((h_m_s_ms >> 56) & 0xff);
  29 + tm->tm_min = bcd2bin((h_m_s_ms >> 48) & 0xff);
  30 + tm->tm_sec = bcd2bin((h_m_s_ms >> 40) & 0xff);
  31 +
  32 + GregorianDay(tm);
  33 +}
  34 +
  35 +unsigned long __init opal_get_boot_time(void)
  36 +{
  37 + struct rtc_time tm;
  38 + u32 y_m_d;
  39 + u64 h_m_s_ms;
  40 + long rc = OPAL_BUSY;
  41 +
  42 + while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
  43 + rc = opal_rtc_read(&y_m_d, &h_m_s_ms);
  44 + if (rc == OPAL_BUSY_EVENT)
  45 + opal_poll_events(NULL);
  46 + else
  47 + mdelay(10);
  48 + }
  49 + if (rc != OPAL_SUCCESS)
  50 + return 0;
  51 + opal_to_tm(y_m_d, h_m_s_ms, &tm);
  52 + return mktime(tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
  53 + tm.tm_hour, tm.tm_min, tm.tm_sec);
  54 +}
  55 +
  56 +void opal_get_rtc_time(struct rtc_time *tm)
  57 +{
  58 + long rc = OPAL_BUSY;
  59 + u32 y_m_d;
  60 + u64 h_m_s_ms;
  61 +
  62 + while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
  63 + rc = opal_rtc_read(&y_m_d, &h_m_s_ms);
  64 + if (rc == OPAL_BUSY_EVENT)
  65 + opal_poll_events(NULL);
  66 + else
  67 + mdelay(10);
  68 + }
  69 + if (rc != OPAL_SUCCESS)
  70 + return;
  71 + opal_to_tm(y_m_d, h_m_s_ms, tm);
  72 +}
  73 +
  74 +int opal_set_rtc_time(struct rtc_time *tm)
  75 +{
  76 + long rc = OPAL_BUSY;
  77 + u32 y_m_d = 0;
  78 + u64 h_m_s_ms = 0;
  79 +
  80 + y_m_d |= ((u32)bin2bcd((tm->tm_year + 1900) / 100)) << 24;
  81 + y_m_d |= ((u32)bin2bcd((tm->tm_year + 1900) % 100)) << 16;
  82 + y_m_d |= ((u32)bin2bcd((tm->tm_mon + 1))) << 8;
  83 + y_m_d |= ((u32)bin2bcd(tm->tm_mday));
  84 +
  85 + h_m_s_ms |= ((u64)bin2bcd(tm->tm_hour)) << 56;
  86 + h_m_s_ms |= ((u64)bin2bcd(tm->tm_min)) << 48;
  87 + h_m_s_ms |= ((u64)bin2bcd(tm->tm_sec)) << 40;
  88 +
  89 + while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
  90 + rc = opal_rtc_write(y_m_d, h_m_s_ms);
  91 + if (rc == OPAL_BUSY_EVENT)
  92 + opal_poll_events(NULL);
  93 + else
  94 + mdelay(10);
  95 + }
  96 + return rc == OPAL_SUCCESS ? 0 : -EIO;
  97 +}
arch/powerpc/platforms/powernv/setup.c
... ... @@ -29,7 +29,9 @@
29 29 #include <asm/machdep.h>
30 30 #include <asm/firmware.h>
31 31 #include <asm/xics.h>
  32 +#include <asm/rtas.h>
32 33 #include <asm/opal.h>
  34 +#include <asm/xics.h>
33 35  
34 36 #include "powernv.h"
35 37  
... ... @@ -40,7 +42,9 @@
40 42  
41 43 /* XXX PCI */
42 44  
43   - /* XXX NVRAM */
  45 + /* Setup RTC and NVRAM callbacks */
  46 + if (firmware_has_feature(FW_FEATURE_OPAL))
  47 + opal_nvram_init();
44 48  
45 49 /* Enable NAP mode */
46 50 powersave_nap = 1;
47 51  
48 52  
49 53  
50 54  
51 55  
52 56  
53 57  
54 58  
55 59  
56 60  
... ... @@ -118,31 +122,41 @@
118 122 pnv_power_off();
119 123 }
120 124  
121   -static unsigned long __init pnv_get_boot_time(void)
  125 +static void pnv_progress(char *s, unsigned short hex)
122 126 {
123   - return 0;
124 127 }
125 128  
126   -static void pnv_get_rtc_time(struct rtc_time *rtc_tm)
  129 +#ifdef CONFIG_KEXEC
  130 +static void pnv_kexec_cpu_down(int crash_shutdown, int secondary)
127 131 {
  132 + xics_kexec_teardown_cpu(secondary);
128 133 }
  134 +#endif /* CONFIG_KEXEC */
129 135  
130   -static int pnv_set_rtc_time(struct rtc_time *tm)
  136 +static void __init pnv_setup_machdep_opal(void)
131 137 {
132   - return 0;
  138 + ppc_md.get_boot_time = opal_get_boot_time;
  139 + ppc_md.get_rtc_time = opal_get_rtc_time;
  140 + ppc_md.set_rtc_time = opal_set_rtc_time;
  141 + ppc_md.restart = pnv_restart;
  142 + ppc_md.power_off = pnv_power_off;
  143 + ppc_md.halt = pnv_halt;
133 144 }
134 145  
135   -static void pnv_progress(char *s, unsigned short hex)
  146 +#ifdef CONFIG_PPC_POWERNV_RTAS
  147 +static void __init pnv_setup_machdep_rtas(void)
136 148 {
  149 + if (rtas_token("get-time-of-day") != RTAS_UNKNOWN_SERVICE) {
  150 + ppc_md.get_boot_time = rtas_get_boot_time;
  151 + ppc_md.get_rtc_time = rtas_get_rtc_time;
  152 + ppc_md.set_rtc_time = rtas_set_rtc_time;
  153 + }
  154 + ppc_md.restart = rtas_restart;
  155 + ppc_md.power_off = rtas_power_off;
  156 + ppc_md.halt = rtas_halt;
137 157 }
  158 +#endif /* CONFIG_PPC_POWERNV_RTAS */
138 159  
139   -#ifdef CONFIG_KEXEC
140   -static void pnv_kexec_cpu_down(int crash_shutdown, int secondary)
141   -{
142   - xics_kexec_teardown_cpu(secondary);
143   -}
144   -#endif /* CONFIG_KEXEC */
145   -
146 160 static int __init pnv_probe(void)
147 161 {
148 162 unsigned long root = of_get_flat_dt_root();
... ... @@ -152,6 +166,13 @@
152 166  
153 167 hpte_init_native();
154 168  
  169 + if (firmware_has_feature(FW_FEATURE_OPAL))
  170 + pnv_setup_machdep_opal();
  171 +#ifdef CONFIG_PPC_POWERNV_RTAS
  172 + else if (rtas.base)
  173 + pnv_setup_machdep_rtas();
  174 +#endif /* CONFIG_PPC_POWERNV_RTAS */
  175 +
155 176 pr_debug("PowerNV detected !\n");
156 177  
157 178 return 1;
158 179  
159 180  
... ... @@ -160,16 +181,10 @@
160 181 define_machine(powernv) {
161 182 .name = "PowerNV",
162 183 .probe = pnv_probe,
163   - .setup_arch = pnv_setup_arch,
164 184 .init_early = pnv_init_early,
  185 + .setup_arch = pnv_setup_arch,
165 186 .init_IRQ = pnv_init_IRQ,
166 187 .show_cpuinfo = pnv_show_cpuinfo,
167   - .restart = pnv_restart,
168   - .power_off = pnv_power_off,
169   - .halt = pnv_halt,
170   - .get_boot_time = pnv_get_boot_time,
171   - .get_rtc_time = pnv_get_rtc_time,
172   - .set_rtc_time = pnv_set_rtc_time,
173 188 .progress = pnv_progress,
174 189 .power_save = power7_idle,
175 190 .calibrate_decr = generic_calibrate_decr,