diff options
author | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2011-05-12 07:46:38 +0400 |
---|---|---|
committer | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2011-06-29 11:48:35 +0400 |
commit | 4d2bb3f5003617cb42b89faefd0009c505c3abd5 (patch) | |
tree | 6c64b00f64d5a3290e89003427cff156c9afa640 /arch/powerpc | |
parent | dd2e356a3dd1fea6d911798044532304c3ef4050 (diff) | |
download | linux-4d2bb3f5003617cb42b89faefd0009c505c3abd5.tar.xz |
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/powerpc')
-rw-r--r-- | arch/powerpc/Kconfig.debug | 15 | ||||
-rw-r--r-- | arch/powerpc/include/asm/udbg.h | 1 | ||||
-rw-r--r-- | arch/powerpc/kernel/udbg.c | 3 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/lpar.c | 189 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/pseries.h | 3 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/setup.c | 5 |
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 Select this to enable early debugging for a machine with a HVC console on vterm 0. +config PPC_EARLY_DEBUG_LPAR_HVSI + bool "LPAR HVSI Console" + depends on PPC_PSERIES + help + Select this to enable early debugging for a machine with a HVSI + console on a specified vterm. + config PPC_EARLY_DEBUG_G5 bool "Apple G5" depends on PPC_PMAC64 @@ -253,6 +260,14 @@ config PPC_EARLY_DEBUG_WSP endchoice +config PPC_EARLY_DEBUG_HVSI_VTERMNO + hex "vterm number to use with early debug HVSI" + depends on PPC_EARLY_DEBUG_LPAR_HVSI + default "0x30000000" + help + You probably want 0x30000000 for your first serial port and + 0x30000001 for your second one + config PPC_EARLY_DEBUG_44x_PHYSLOW hex "Low 32 bits of early debug UART physical address" 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); extern void __init udbg_early_init(void); extern void __init udbg_init_debug_lpar(void); +extern void __init udbg_init_debug_lpar_hvsi(void); extern void __init udbg_init_pmac_realmode(void); extern void __init udbg_init_maple_realmode(void); extern 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) #if defined(CONFIG_PPC_EARLY_DEBUG_LPAR) /* For LPAR machines that have an HVC console on vterm 0 */ udbg_init_debug_lpar(); +#elif defined(CONFIG_PPC_EARLY_DEBUG_LPAR_HVSI) + /* For LPAR machines that have an HVSI console on vterm 0 */ + udbg_init_debug_lpar_hvsi(); #elif defined(CONFIG_PPC_EARLY_DEBUG_G5) /* For use on Apple G5 machines */ 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); extern void pSeries_find_serial_port(void); - -static int vtermno; /* virtual terminal# for udbg */ - -#define __ALIGNED__ __attribute__((__aligned__(sizeof(long)))) -static void udbg_hvsi_putc(char c) -{ - /* packet's seqno isn't used anyways */ - uint8_t packet[] __ALIGNED__ = { 0xff, 5, 0, 0, c }; - int rc; - - if (c == '\n') - udbg_hvsi_putc('\r'); - - do { - rc = plpar_put_term_char(vtermno, sizeof(packet), packet); - } while (rc == H_BUSY); -} - -static long hvsi_udbg_buf_len; -static uint8_t hvsi_udbg_buf[256]; - -static int udbg_hvsi_getc_poll(void) -{ - unsigned char ch; - int rc, i; - - if (hvsi_udbg_buf_len == 0) { - rc = plpar_get_term_char(vtermno, &hvsi_udbg_buf_len, hvsi_udbg_buf); - if (rc != H_SUCCESS || hvsi_udbg_buf[0] != 0xff) { - /* bad read or non-data packet */ - hvsi_udbg_buf_len = 0; - } else { - /* remove the packet header */ - for (i = 4; i < hvsi_udbg_buf_len; i++) - hvsi_udbg_buf[i-4] = hvsi_udbg_buf[i]; - hvsi_udbg_buf_len -= 4; - } - } - - if (hvsi_udbg_buf_len <= 0 || hvsi_udbg_buf_len > 256) { - /* no data ready */ - hvsi_udbg_buf_len = 0; - return -1; - } - - ch = hvsi_udbg_buf[0]; - /* shift remaining data down */ - for (i = 1; i < hvsi_udbg_buf_len; i++) { - hvsi_udbg_buf[i-1] = hvsi_udbg_buf[i]; - } - hvsi_udbg_buf_len--; - - return ch; -} - -static int udbg_hvsi_getc(void) -{ - int ch; - for (;;) { - ch = udbg_hvsi_getc_poll(); - if (ch == -1) { - /* This shouldn't be needed...but... */ - volatile unsigned long delay; - for (delay=0; delay < 2000000; delay++) - ; - } else { - return ch; - } - } -} - -static void udbg_putcLP(char c) -{ - char buf[16]; - unsigned long rc; - - if (c == '\n') - udbg_putcLP('\r'); - - buf[0] = c; - do { - rc = plpar_put_term_char(vtermno, 1, buf); - } while(rc == H_BUSY); -} - -/* Buffered chars getc */ -static long inbuflen; -static long inbuf[2]; /* must be 2 longs */ - -static int udbg_getc_pollLP(void) -{ - /* The interface is tricky because it may return up to 16 chars. - * We save them statically for future calls to udbg_getc(). - */ - char ch, *buf = (char *)inbuf; - int i; - long rc; - if (inbuflen == 0) { - /* get some more chars. */ - inbuflen = 0; - rc = plpar_get_term_char(vtermno, &inbuflen, buf); - if (rc != H_SUCCESS) - inbuflen = 0; /* otherwise inbuflen is garbage */ - } - if (inbuflen <= 0 || inbuflen > 16) { - /* Catch error case as well as other oddities (corruption) */ - inbuflen = 0; - return -1; - } - ch = buf[0]; - for (i = 1; i < inbuflen; i++) /* shuffle them down. */ - buf[i-1] = buf[i]; - inbuflen--; - return ch; -} - -static int udbg_getcLP(void) -{ - int ch; - for (;;) { - ch = udbg_getc_pollLP(); - if (ch == -1) { - /* This shouldn't be needed...but... */ - volatile unsigned long delay; - for (delay=0; delay < 2000000; delay++) - ; - } else { - return ch; - } - } -} - -/* call this from early_init() for a working debug console on - * vterm capable LPAR machines - */ -void __init udbg_init_debug_lpar(void) -{ - vtermno = 0; - udbg_putc = udbg_putcLP; - udbg_getc = udbg_getcLP; - udbg_getc_poll = udbg_getc_pollLP; -} - -/* returns 0 if couldn't find or use /chosen/stdout as console */ -void __init find_udbg_vterm(void) -{ - struct device_node *stdout_node; - const u32 *termno; - const char *name; - - /* find the boot console from /chosen/stdout */ - if (!of_chosen) - return; - name = of_get_property(of_chosen, "linux,stdout-path", NULL); - if (name == NULL) - return; - stdout_node = of_find_node_by_path(name); - if (!stdout_node) - return; - name = of_get_property(stdout_node, "name", NULL); - if (!name) { - printk(KERN_WARNING "stdout node missing 'name' property!\n"); - goto out; - } - - /* Check if it's a virtual terminal */ - if (strncmp(name, "vty", 3) != 0) - goto out; - termno = of_get_property(stdout_node, "reg", NULL); - if (termno == NULL) - goto out; - vtermno = termno[0]; - - if (of_device_is_compatible(stdout_node, "hvterm1")) { - udbg_putc = udbg_putcLP; - udbg_getc = udbg_getcLP; - udbg_getc_poll = udbg_getc_pollLP; - add_preferred_console("hvc", termno[0] & 0xff, NULL); - } else if (of_device_is_compatible(stdout_node, "hvterm-protocol")) { - vtermno = termno[0]; - udbg_putc = udbg_hvsi_putc; - udbg_getc = udbg_hvsi_getc; - udbg_getc_poll = udbg_hvsi_getc_poll; - add_preferred_console("hvsi", termno[0] & 0xff, NULL); - } -out: - of_node_put(stdout_node); -} - void vpa_init(int cpu) { 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); /* Poweron flag used for enabling auto ups restart */ extern unsigned long rtas_poweron_auto; -extern void find_udbg_vterm(void); +/* Provided by HVC VIO */ +extern void hvc_vio_init_early(void); /* Dynamic logical Partitioning/Mobility */ extern 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) { pr_debug(" -> pSeries_init_early()\n"); +#ifdef CONFIG_HVC_CONSOLE if (firmware_has_feature(FW_FEATURE_LPAR)) - find_udbg_vterm(); - + hvc_vio_init_early(); +#endif if (firmware_has_feature(FW_FEATURE_DABR)) ppc_md.set_dabr = pseries_set_dabr; else if (firmware_has_feature(FW_FEATURE_XDABR)) |