aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorBenjamin Herrenschmidt <benh@kernel.crashing.org>2011-05-11 23:46:38 -0400
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2011-06-29 03:48:35 -0400
commit4d2bb3f5003617cb42b89faefd0009c505c3abd5 (patch)
tree6c64b00f64d5a3290e89003427cff156c9afa640 /arch
parentdd2e356a3dd1fea6d911798044532304c3ef4050 (diff)
powerpc/pseries: Re-implement HVSI as part of hvc_vio
On pseries machines, consoles are provided by the hypervisor using a low level get_chars/put_chars type interface. However, this is really just a transport to the service processor which implements them either as "raw" console (networked consoles, HMC, ...) or as "hvsi" serial ports. The later is a simple packet protocol on top of the raw character interface that is supposed to convey additional "serial port" style semantics. In practice however, all it does is provide a way to read the CD line and set/clear our DTR line, that's it. We currently implement the "raw" protocol as an hvc console backend (/dev/hvcN) and the "hvsi" protocol using a separate tty driver (/dev/hvsi0). However this is quite impractical. The arbitrary difference between the two type of devices has been a major source of user (and distro) confusion. Additionally, there's an additional mini -hvsi implementation in the pseries platform code for our low level debug console and early boot kernel messages, which means code duplication, though that low level variant is impractical as it's incapable of doing the initial protocol negociation to establish the link to the FSP. This essentially replaces the dedicated hvsi driver and the platform udbg code completely by extending the existing hvc_vio backend used in "raw" mode so that: - It now supports HVSI as well - We add support for hvc backend providing tiocm{get,set} - It also provides a udbg interface for early debug and boot console This is overall less code, though this will only be obvious once we remove the old "hvsi" driver, which is still available for now. When the old driver is enabled, the new code still kicks in for the low level udbg console, replacing the old mini implementation in the platform code, it just doesn't provide the higher level "hvc" interface. In addition to producing generally simler code, this has several benefits over our current situation: - The user/distro only has to deal with /dev/hvcN for the hypervisor console, avoiding all sort of confusion that has plagued us in the past - The tty, kernel and low level debug console all use the same code base which supports the full protocol establishment process, thus the console is now available much earlier than it used to be with the old HVSI driver. The kernel console works much earlier and udbg is available much earlier too. Hackers can enable a hard coded very-early debug console as well that works with HVSI (previously that was only supported for the "raw" mode). I've tried to keep the same semantics as hvsi relative to how I react to things like CD changes, with some subtle differences though: - I clear DTR on close if HUPCL is set - Current hvsi triggers a hangup if it detects a up->down transition on CD (you can still open a console with CD down). My new implementation triggers a hangup if the link to the FSP is severed, and severs it upon detecting a up->down transition on CD. Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'arch')
-rw-r--r--arch/powerpc/Kconfig.debug15
-rw-r--r--arch/powerpc/include/asm/udbg.h1
-rw-r--r--arch/powerpc/kernel/udbg.c3
-rw-r--r--arch/powerpc/platforms/pseries/lpar.c189
-rw-r--r--arch/powerpc/platforms/pseries/pseries.h3
-rw-r--r--arch/powerpc/platforms/pseries/setup.c5
6 files changed, 24 insertions, 192 deletions
diff --git a/arch/powerpc/Kconfig.debug b/arch/powerpc/Kconfig.debug
index e72dcf6a421d..067cb8480747 100644
--- a/arch/powerpc/Kconfig.debug
+++ b/arch/powerpc/Kconfig.debug
@@ -167,6 +167,13 @@ config PPC_EARLY_DEBUG_LPAR
167 Select this to enable early debugging for a machine with a HVC 167 Select this to enable early debugging for a machine with a HVC
168 console on vterm 0. 168 console on vterm 0.
169 169
170config PPC_EARLY_DEBUG_LPAR_HVSI
171 bool "LPAR HVSI Console"
172 depends on PPC_PSERIES
173 help
174 Select this to enable early debugging for a machine with a HVSI
175 console on a specified vterm.
176
170config PPC_EARLY_DEBUG_G5 177config PPC_EARLY_DEBUG_G5
171 bool "Apple G5" 178 bool "Apple G5"
172 depends on PPC_PMAC64 179 depends on PPC_PMAC64
@@ -253,6 +260,14 @@ config PPC_EARLY_DEBUG_WSP
253 260
254endchoice 261endchoice
255 262
263config PPC_EARLY_DEBUG_HVSI_VTERMNO
264 hex "vterm number to use with early debug HVSI"
265 depends on PPC_EARLY_DEBUG_LPAR_HVSI
266 default "0x30000000"
267 help
268 You probably want 0x30000000 for your first serial port and
269 0x30000001 for your second one
270
256config PPC_EARLY_DEBUG_44x_PHYSLOW 271config PPC_EARLY_DEBUG_44x_PHYSLOW
257 hex "Low 32 bits of early debug UART physical address" 272 hex "Low 32 bits of early debug UART physical address"
258 depends on PPC_EARLY_DEBUG_44x 273 depends on PPC_EARLY_DEBUG_44x
diff --git a/arch/powerpc/include/asm/udbg.h b/arch/powerpc/include/asm/udbg.h
index 58580e94a2bb..93e05d1b34b2 100644
--- a/arch/powerpc/include/asm/udbg.h
+++ b/arch/powerpc/include/asm/udbg.h
@@ -40,6 +40,7 @@ extern void udbg_adb_init_early(void);
40 40
41extern void __init udbg_early_init(void); 41extern void __init udbg_early_init(void);
42extern void __init udbg_init_debug_lpar(void); 42extern void __init udbg_init_debug_lpar(void);
43extern void __init udbg_init_debug_lpar_hvsi(void);
43extern void __init udbg_init_pmac_realmode(void); 44extern void __init udbg_init_pmac_realmode(void);
44extern void __init udbg_init_maple_realmode(void); 45extern void __init udbg_init_maple_realmode(void);
45extern void __init udbg_init_pas_realmode(void); 46extern void __init udbg_init_pas_realmode(void);
diff --git a/arch/powerpc/kernel/udbg.c b/arch/powerpc/kernel/udbg.c
index a57e61ea0558..faa82c1f3f68 100644
--- a/arch/powerpc/kernel/udbg.c
+++ b/arch/powerpc/kernel/udbg.c
@@ -31,6 +31,9 @@ void __init udbg_early_init(void)
31#if defined(CONFIG_PPC_EARLY_DEBUG_LPAR) 31#if defined(CONFIG_PPC_EARLY_DEBUG_LPAR)
32 /* For LPAR machines that have an HVC console on vterm 0 */ 32 /* For LPAR machines that have an HVC console on vterm 0 */
33 udbg_init_debug_lpar(); 33 udbg_init_debug_lpar();
34#elif defined(CONFIG_PPC_EARLY_DEBUG_LPAR_HVSI)
35 /* For LPAR machines that have an HVSI console on vterm 0 */
36 udbg_init_debug_lpar_hvsi();
34#elif defined(CONFIG_PPC_EARLY_DEBUG_G5) 37#elif defined(CONFIG_PPC_EARLY_DEBUG_G5)
35 /* For use on Apple G5 machines */ 38 /* For use on Apple G5 machines */
36 udbg_init_pmac_realmode(); 39 udbg_init_pmac_realmode();
diff --git a/arch/powerpc/platforms/pseries/lpar.c b/arch/powerpc/platforms/pseries/lpar.c
index e3a96c4348ab..f7205d344efd 100644
--- a/arch/powerpc/platforms/pseries/lpar.c
+++ b/arch/powerpc/platforms/pseries/lpar.c
@@ -52,195 +52,6 @@ EXPORT_SYMBOL(plpar_hcall_norets);
52 52
53extern void pSeries_find_serial_port(void); 53extern void pSeries_find_serial_port(void);
54 54
55
56static int vtermno; /* virtual terminal# for udbg */
57
58#define __ALIGNED__ __attribute__((__aligned__(sizeof(long))))
59static void udbg_hvsi_putc(char c)
60{
61 /* packet's seqno isn't used anyways */
62 uint8_t packet[] __ALIGNED__ = { 0xff, 5, 0, 0, c };
63 int rc;
64
65 if (c == '\n')
66 udbg_hvsi_putc('\r');
67
68 do {
69 rc = plpar_put_term_char(vtermno, sizeof(packet), packet);
70 } while (rc == H_BUSY);
71}
72
73static long hvsi_udbg_buf_len;
74static uint8_t hvsi_udbg_buf[256];
75
76static int udbg_hvsi_getc_poll(void)
77{
78 unsigned char ch;
79 int rc, i;
80
81 if (hvsi_udbg_buf_len == 0) {
82 rc = plpar_get_term_char(vtermno, &hvsi_udbg_buf_len, hvsi_udbg_buf);
83 if (rc != H_SUCCESS || hvsi_udbg_buf[0] != 0xff) {
84 /* bad read or non-data packet */
85 hvsi_udbg_buf_len = 0;
86 } else {
87 /* remove the packet header */
88 for (i = 4; i < hvsi_udbg_buf_len; i++)
89 hvsi_udbg_buf[i-4] = hvsi_udbg_buf[i];
90 hvsi_udbg_buf_len -= 4;
91 }
92 }
93
94 if (hvsi_udbg_buf_len <= 0 || hvsi_udbg_buf_len > 256) {
95 /* no data ready */
96 hvsi_udbg_buf_len = 0;
97 return -1;
98 }
99
100 ch = hvsi_udbg_buf[0];
101 /* shift remaining data down */
102 for (i = 1; i < hvsi_udbg_buf_len; i++) {
103 hvsi_udbg_buf[i-1] = hvsi_udbg_buf[i];
104 }
105 hvsi_udbg_buf_len--;
106
107 return ch;
108}
109
110static int udbg_hvsi_getc(void)
111{
112 int ch;
113 for (;;) {
114 ch = udbg_hvsi_getc_poll();
115 if (ch == -1) {
116 /* This shouldn't be needed...but... */
117 volatile unsigned long delay;
118 for (delay=0; delay < 2000000; delay++)
119 ;
120 } else {
121 return ch;
122 }
123 }
124}
125
126static void udbg_putcLP(char c)
127{
128 char buf[16];
129 unsigned long rc;
130
131 if (c == '\n')
132 udbg_putcLP('\r');
133
134 buf[0] = c;
135 do {
136 rc = plpar_put_term_char(vtermno, 1, buf);
137 } while(rc == H_BUSY);
138}
139
140/* Buffered chars getc */
141static long inbuflen;
142static long inbuf[2]; /* must be 2 longs */
143
144static int udbg_getc_pollLP(void)
145{
146 /* The interface is tricky because it may return up to 16 chars.
147 * We save them statically for future calls to udbg_getc().
148 */
149 char ch, *buf = (char *)inbuf;
150 int i;
151 long rc;
152 if (inbuflen == 0) {
153 /* get some more chars. */
154 inbuflen = 0;
155 rc = plpar_get_term_char(vtermno, &inbuflen, buf);
156 if (rc != H_SUCCESS)
157 inbuflen = 0; /* otherwise inbuflen is garbage */
158 }
159 if (inbuflen <= 0 || inbuflen > 16) {
160 /* Catch error case as well as other oddities (corruption) */
161 inbuflen = 0;
162 return -1;
163 }
164 ch = buf[0];
165 for (i = 1; i < inbuflen; i++) /* shuffle them down. */
166 buf[i-1] = buf[i];
167 inbuflen--;
168 return ch;
169}
170
171static int udbg_getcLP(void)
172{
173 int ch;
174 for (;;) {
175 ch = udbg_getc_pollLP();
176 if (ch == -1) {
177 /* This shouldn't be needed...but... */
178 volatile unsigned long delay;
179 for (delay=0; delay < 2000000; delay++)
180 ;
181 } else {
182 return ch;
183 }
184 }
185}
186
187/* call this from early_init() for a working debug console on
188 * vterm capable LPAR machines
189 */
190void __init udbg_init_debug_lpar(void)
191{
192 vtermno = 0;
193 udbg_putc = udbg_putcLP;
194 udbg_getc = udbg_getcLP;
195 udbg_getc_poll = udbg_getc_pollLP;
196}
197
198/* returns 0 if couldn't find or use /chosen/stdout as console */
199void __init find_udbg_vterm(void)
200{
201 struct device_node *stdout_node;
202 const u32 *termno;
203 const char *name;
204
205 /* find the boot console from /chosen/stdout */
206 if (!of_chosen)
207 return;
208 name = of_get_property(of_chosen, "linux,stdout-path", NULL);
209 if (name == NULL)
210 return;
211 stdout_node = of_find_node_by_path(name);
212 if (!stdout_node)
213 return;
214 name = of_get_property(stdout_node, "name", NULL);
215 if (!name) {
216 printk(KERN_WARNING "stdout node missing 'name' property!\n");
217 goto out;
218 }
219
220 /* Check if it's a virtual terminal */
221 if (strncmp(name, "vty", 3) != 0)
222 goto out;
223 termno = of_get_property(stdout_node, "reg", NULL);
224 if (termno == NULL)
225 goto out;
226 vtermno = termno[0];
227
228 if (of_device_is_compatible(stdout_node, "hvterm1")) {
229 udbg_putc = udbg_putcLP;
230 udbg_getc = udbg_getcLP;
231 udbg_getc_poll = udbg_getc_pollLP;
232 add_preferred_console("hvc", termno[0] & 0xff, NULL);
233 } else if (of_device_is_compatible(stdout_node, "hvterm-protocol")) {
234 vtermno = termno[0];
235 udbg_putc = udbg_hvsi_putc;
236 udbg_getc = udbg_hvsi_getc;
237 udbg_getc_poll = udbg_hvsi_getc_poll;
238 add_preferred_console("hvsi", termno[0] & 0xff, NULL);
239 }
240out:
241 of_node_put(stdout_node);
242}
243
244void vpa_init(int cpu) 55void vpa_init(int cpu)
245{ 56{
246 int hwcpu = get_hard_smp_processor_id(cpu); 57 int hwcpu = get_hard_smp_processor_id(cpu);
diff --git a/arch/powerpc/platforms/pseries/pseries.h b/arch/powerpc/platforms/pseries/pseries.h
index e9f6d2859c3c..24c7162f11d9 100644
--- a/arch/powerpc/platforms/pseries/pseries.h
+++ b/arch/powerpc/platforms/pseries/pseries.h
@@ -47,7 +47,8 @@ extern void pSeries_final_fixup(void);
47/* Poweron flag used for enabling auto ups restart */ 47/* Poweron flag used for enabling auto ups restart */
48extern unsigned long rtas_poweron_auto; 48extern unsigned long rtas_poweron_auto;
49 49
50extern void find_udbg_vterm(void); 50/* Provided by HVC VIO */
51extern void hvc_vio_init_early(void);
51 52
52/* Dynamic logical Partitioning/Mobility */ 53/* Dynamic logical Partitioning/Mobility */
53extern void dlpar_free_cc_nodes(struct device_node *); 54extern void dlpar_free_cc_nodes(struct device_node *);
diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c
index 593acceeff96..d00e52926b71 100644
--- a/arch/powerpc/platforms/pseries/setup.c
+++ b/arch/powerpc/platforms/pseries/setup.c
@@ -512,9 +512,10 @@ static void __init pSeries_init_early(void)
512{ 512{
513 pr_debug(" -> pSeries_init_early()\n"); 513 pr_debug(" -> pSeries_init_early()\n");
514 514
515#ifdef CONFIG_HVC_CONSOLE
515 if (firmware_has_feature(FW_FEATURE_LPAR)) 516 if (firmware_has_feature(FW_FEATURE_LPAR))
516 find_udbg_vterm(); 517 hvc_vio_init_early();
517 518#endif
518 if (firmware_has_feature(FW_FEATURE_DABR)) 519 if (firmware_has_feature(FW_FEATURE_DABR))
519 ppc_md.set_dabr = pseries_set_dabr; 520 ppc_md.set_dabr = pseries_set_dabr;
520 else if (firmware_has_feature(FW_FEATURE_XDABR)) 521 else if (firmware_has_feature(FW_FEATURE_XDABR))