diff options
author | David S. Miller <davem@sunset.davemloft.net> | 2007-05-15 20:03:54 -0400 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2007-05-15 23:23:02 -0400 |
commit | c7754d465b1feade85b5f1c4492781a30f6652a2 (patch) | |
tree | 9a3b6ccb18983c1ea389377028ca51c8170730ef | |
parent | 7b104bcb8e460e45a1aebe3da9b86aacdb4cab12 (diff) |
[SPARC64]: Add hypervisor API negotiation and fix console bugs.
Hypervisor interfaces need to be negotiated in order to use
some API calls reliably. So add a small set of interfaces
to request API versions and query current settings.
This allows us to fix some bugs in the hypervisor console:
1) If we can negotiate API group CORE of at least major 1
minor 1 we can use con_read and con_write which can improve
console performance quite a bit.
2) When we do a console write request, we should hold the
spinlock around the whole request, not a byte at a time.
What would happen is that it's easy for output from
different cpus to get mixed with each other.
3) Use consistent udelay() based polling, udelay(1) each
loop with a limit of 1000 polls to handle stuck hypervisor
console.
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | arch/sparc64/kernel/Makefile | 2 | ||||
-rw-r--r-- | arch/sparc64/kernel/entry.S | 94 | ||||
-rw-r--r-- | arch/sparc64/kernel/hvapi.c | 189 | ||||
-rw-r--r-- | arch/sparc64/kernel/setup.c | 3 | ||||
-rw-r--r-- | drivers/serial/sunhv.c | 276 | ||||
-rw-r--r-- | include/asm-sparc64/hypervisor.h | 83 |
6 files changed, 574 insertions, 73 deletions
diff --git a/arch/sparc64/kernel/Makefile b/arch/sparc64/kernel/Makefile index 6bf6fb65bc20..c749dccacc32 100644 --- a/arch/sparc64/kernel/Makefile +++ b/arch/sparc64/kernel/Makefile | |||
@@ -12,7 +12,7 @@ obj-y := process.o setup.o cpu.o idprom.o \ | |||
12 | irq.o ptrace.o time.o sys_sparc.o signal.o \ | 12 | irq.o ptrace.o time.o sys_sparc.o signal.o \ |
13 | unaligned.o central.o pci.o starfire.o semaphore.o \ | 13 | unaligned.o central.o pci.o starfire.o semaphore.o \ |
14 | power.o sbus.o iommu_common.o sparc64_ksyms.o chmc.o \ | 14 | power.o sbus.o iommu_common.o sparc64_ksyms.o chmc.o \ |
15 | visemul.o prom.o of_device.o | 15 | visemul.o prom.o of_device.o hvapi.o |
16 | 16 | ||
17 | obj-$(CONFIG_STACKTRACE) += stacktrace.o | 17 | obj-$(CONFIG_STACKTRACE) += stacktrace.o |
18 | obj-$(CONFIG_PCI) += ebus.o isa.o pci_common.o pci_iommu.o \ | 18 | obj-$(CONFIG_PCI) += ebus.o isa.o pci_common.o pci_iommu.o \ |
diff --git a/arch/sparc64/kernel/entry.S b/arch/sparc64/kernel/entry.S index c15a3edcb826..732b77cb71f8 100644 --- a/arch/sparc64/kernel/entry.S +++ b/arch/sparc64/kernel/entry.S | |||
@@ -1843,3 +1843,97 @@ sun4v_cpu_state: | |||
1843 | mov %o1, %o0 | 1843 | mov %o1, %o0 |
1844 | 1: retl | 1844 | 1: retl |
1845 | nop | 1845 | nop |
1846 | |||
1847 | /* %o0: API group number | ||
1848 | * %o1: pointer to unsigned long major number storage | ||
1849 | * %o2: pointer to unsigned long minor number storage | ||
1850 | * | ||
1851 | * returns %o0: status | ||
1852 | */ | ||
1853 | .globl sun4v_get_version | ||
1854 | sun4v_get_version: | ||
1855 | mov HV_CORE_GET_VER, %o5 | ||
1856 | mov %o1, %o3 | ||
1857 | mov %o2, %o4 | ||
1858 | ta HV_CORE_TRAP | ||
1859 | stx %o1, [%o3] | ||
1860 | retl | ||
1861 | stx %o2, [%o4] | ||
1862 | |||
1863 | /* %o0: API group number | ||
1864 | * %o1: desired major number | ||
1865 | * %o2: desired minor number | ||
1866 | * %o3: pointer to unsigned long actual minor number storage | ||
1867 | * | ||
1868 | * returns %o0: status | ||
1869 | */ | ||
1870 | .globl sun4v_set_version | ||
1871 | sun4v_set_version: | ||
1872 | mov HV_CORE_SET_VER, %o5 | ||
1873 | mov %o3, %o4 | ||
1874 | ta HV_CORE_TRAP | ||
1875 | retl | ||
1876 | stx %o1, [%o4] | ||
1877 | |||
1878 | /* %o0: pointer to unsigned long status | ||
1879 | * | ||
1880 | * returns %o0: signed character | ||
1881 | */ | ||
1882 | .globl sun4v_con_getchar | ||
1883 | sun4v_con_getchar: | ||
1884 | mov %o0, %o4 | ||
1885 | mov HV_FAST_CONS_GETCHAR, %o5 | ||
1886 | clr %o0 | ||
1887 | clr %o1 | ||
1888 | ta HV_FAST_TRAP | ||
1889 | stx %o0, [%o4] | ||
1890 | retl | ||
1891 | sra %o1, 0, %o0 | ||
1892 | |||
1893 | /* %o0: signed long character | ||
1894 | * | ||
1895 | * returns %o0: status | ||
1896 | */ | ||
1897 | .globl sun4v_con_putchar | ||
1898 | sun4v_con_putchar: | ||
1899 | mov HV_FAST_CONS_PUTCHAR, %o5 | ||
1900 | ta HV_FAST_TRAP | ||
1901 | retl | ||
1902 | sra %o0, 0, %o0 | ||
1903 | |||
1904 | /* %o0: buffer real address | ||
1905 | * %o1: buffer size | ||
1906 | * %o2: pointer to unsigned long bytes_read | ||
1907 | * | ||
1908 | * returns %o0: status | ||
1909 | */ | ||
1910 | .globl sun4v_con_read | ||
1911 | sun4v_con_read: | ||
1912 | mov %o2, %o4 | ||
1913 | mov HV_FAST_CONS_READ, %o5 | ||
1914 | ta HV_FAST_TRAP | ||
1915 | brnz %o0, 1f | ||
1916 | cmp %o1, -1 /* break */ | ||
1917 | be,a,pn %icc, 1f | ||
1918 | mov %o1, %o0 | ||
1919 | cmp %o1, -2 /* hup */ | ||
1920 | be,a,pn %icc, 1f | ||
1921 | mov %o1, %o0 | ||
1922 | stx %o1, [%o4] | ||
1923 | 1: retl | ||
1924 | nop | ||
1925 | |||
1926 | /* %o0: buffer real address | ||
1927 | * %o1: buffer size | ||
1928 | * %o2: pointer to unsigned long bytes_written | ||
1929 | * | ||
1930 | * returns %o0: status | ||
1931 | */ | ||
1932 | .globl sun4v_con_write | ||
1933 | sun4v_con_write: | ||
1934 | mov %o2, %o4 | ||
1935 | mov HV_FAST_CONS_WRITE, %o5 | ||
1936 | ta HV_FAST_TRAP | ||
1937 | stx %o1, [%o4] | ||
1938 | retl | ||
1939 | nop | ||
diff --git a/arch/sparc64/kernel/hvapi.c b/arch/sparc64/kernel/hvapi.c new file mode 100644 index 000000000000..f03ffc829c7a --- /dev/null +++ b/arch/sparc64/kernel/hvapi.c | |||
@@ -0,0 +1,189 @@ | |||
1 | /* hvapi.c: Hypervisor API management. | ||
2 | * | ||
3 | * Copyright (C) 2007 David S. Miller <davem@davemloft.net> | ||
4 | */ | ||
5 | #include <linux/kernel.h> | ||
6 | #include <linux/module.h> | ||
7 | #include <linux/init.h> | ||
8 | #include <linux/slab.h> | ||
9 | |||
10 | #include <asm/hypervisor.h> | ||
11 | #include <asm/oplib.h> | ||
12 | |||
13 | /* If the hypervisor indicates that the API setting | ||
14 | * calls are unsupported, by returning HV_EBADTRAP or | ||
15 | * HV_ENOTSUPPORTED, we assume that API groups with the | ||
16 | * PRE_API flag set are major 1 minor 0. | ||
17 | */ | ||
18 | struct api_info { | ||
19 | unsigned long group; | ||
20 | unsigned long major; | ||
21 | unsigned long minor; | ||
22 | unsigned int refcnt; | ||
23 | unsigned int flags; | ||
24 | #define FLAG_PRE_API 0x00000001 | ||
25 | }; | ||
26 | |||
27 | static struct api_info api_table[] = { | ||
28 | { .group = HV_GRP_SUN4V, .flags = FLAG_PRE_API }, | ||
29 | { .group = HV_GRP_CORE, .flags = FLAG_PRE_API }, | ||
30 | { .group = HV_GRP_INTR, }, | ||
31 | { .group = HV_GRP_SOFT_STATE, }, | ||
32 | { .group = HV_GRP_PCI, .flags = FLAG_PRE_API }, | ||
33 | { .group = HV_GRP_LDOM, }, | ||
34 | { .group = HV_GRP_SVC_CHAN, .flags = FLAG_PRE_API }, | ||
35 | { .group = HV_GRP_NCS, .flags = FLAG_PRE_API }, | ||
36 | { .group = HV_GRP_NIAG_PERF, .flags = FLAG_PRE_API }, | ||
37 | { .group = HV_GRP_FIRE_PERF, }, | ||
38 | { .group = HV_GRP_DIAG, .flags = FLAG_PRE_API }, | ||
39 | }; | ||
40 | |||
41 | static DEFINE_SPINLOCK(hvapi_lock); | ||
42 | |||
43 | static struct api_info *__get_info(unsigned long group) | ||
44 | { | ||
45 | int i; | ||
46 | |||
47 | for (i = 0; i < ARRAY_SIZE(api_table); i++) { | ||
48 | if (api_table[i].group == group) | ||
49 | return &api_table[i]; | ||
50 | } | ||
51 | return NULL; | ||
52 | } | ||
53 | |||
54 | static void __get_ref(struct api_info *p) | ||
55 | { | ||
56 | p->refcnt++; | ||
57 | } | ||
58 | |||
59 | static void __put_ref(struct api_info *p) | ||
60 | { | ||
61 | if (--p->refcnt == 0) { | ||
62 | unsigned long ignore; | ||
63 | |||
64 | sun4v_set_version(p->group, 0, 0, &ignore); | ||
65 | p->major = p->minor = 0; | ||
66 | } | ||
67 | } | ||
68 | |||
69 | /* Register a hypervisor API specification. It indicates the | ||
70 | * API group and desired major+minor. | ||
71 | * | ||
72 | * If an existing API registration exists '0' (success) will | ||
73 | * be returned if it is compatible with the one being registered. | ||
74 | * Otherwise a negative error code will be returned. | ||
75 | * | ||
76 | * Otherwise an attempt will be made to negotiate the requested | ||
77 | * API group/major/minor with the hypervisor, and errors returned | ||
78 | * if that does not succeed. | ||
79 | */ | ||
80 | int sun4v_hvapi_register(unsigned long group, unsigned long major, | ||
81 | unsigned long *minor) | ||
82 | { | ||
83 | struct api_info *p; | ||
84 | unsigned long flags; | ||
85 | int ret; | ||
86 | |||
87 | spin_lock_irqsave(&hvapi_lock, flags); | ||
88 | p = __get_info(group); | ||
89 | ret = -EINVAL; | ||
90 | if (p) { | ||
91 | if (p->refcnt) { | ||
92 | ret = -EINVAL; | ||
93 | if (p->major == major) { | ||
94 | *minor = p->minor; | ||
95 | ret = 0; | ||
96 | } | ||
97 | } else { | ||
98 | unsigned long actual_minor; | ||
99 | unsigned long hv_ret; | ||
100 | |||
101 | hv_ret = sun4v_set_version(group, major, *minor, | ||
102 | &actual_minor); | ||
103 | ret = -EINVAL; | ||
104 | if (hv_ret == HV_EOK) { | ||
105 | *minor = actual_minor; | ||
106 | p->major = major; | ||
107 | p->minor = actual_minor; | ||
108 | ret = 0; | ||
109 | } else if (hv_ret == HV_EBADTRAP || | ||
110 | HV_ENOTSUPPORTED) { | ||
111 | if (p->flags & FLAG_PRE_API) { | ||
112 | if (major == 1) { | ||
113 | p->major = 1; | ||
114 | p->minor = 0; | ||
115 | *minor = 0; | ||
116 | ret = 0; | ||
117 | } | ||
118 | } | ||
119 | } | ||
120 | } | ||
121 | |||
122 | if (ret == 0) | ||
123 | __get_ref(p); | ||
124 | } | ||
125 | spin_unlock_irqrestore(&hvapi_lock, flags); | ||
126 | |||
127 | return ret; | ||
128 | } | ||
129 | EXPORT_SYMBOL(sun4v_hvapi_register); | ||
130 | |||
131 | void sun4v_hvapi_unregister(unsigned long group) | ||
132 | { | ||
133 | struct api_info *p; | ||
134 | unsigned long flags; | ||
135 | |||
136 | spin_lock_irqsave(&hvapi_lock, flags); | ||
137 | p = __get_info(group); | ||
138 | if (p) | ||
139 | __put_ref(p); | ||
140 | spin_unlock_irqrestore(&hvapi_lock, flags); | ||
141 | } | ||
142 | EXPORT_SYMBOL(sun4v_hvapi_unregister); | ||
143 | |||
144 | int sun4v_hvapi_get(unsigned long group, | ||
145 | unsigned long *major, | ||
146 | unsigned long *minor) | ||
147 | { | ||
148 | struct api_info *p; | ||
149 | unsigned long flags; | ||
150 | int ret; | ||
151 | |||
152 | spin_lock_irqsave(&hvapi_lock, flags); | ||
153 | ret = -EINVAL; | ||
154 | p = __get_info(group); | ||
155 | if (p && p->refcnt) { | ||
156 | *major = p->major; | ||
157 | *minor = p->minor; | ||
158 | ret = 0; | ||
159 | } | ||
160 | spin_unlock_irqrestore(&hvapi_lock, flags); | ||
161 | |||
162 | return ret; | ||
163 | } | ||
164 | EXPORT_SYMBOL(sun4v_hvapi_get); | ||
165 | |||
166 | void __init sun4v_hvapi_init(void) | ||
167 | { | ||
168 | unsigned long group, major, minor; | ||
169 | |||
170 | group = HV_GRP_SUN4V; | ||
171 | major = 1; | ||
172 | minor = 0; | ||
173 | if (sun4v_hvapi_register(group, major, &minor)) | ||
174 | goto bad; | ||
175 | |||
176 | group = HV_GRP_CORE; | ||
177 | major = 1; | ||
178 | minor = 1; | ||
179 | if (sun4v_hvapi_register(group, major, &minor)) | ||
180 | goto bad; | ||
181 | |||
182 | return; | ||
183 | |||
184 | bad: | ||
185 | prom_printf("HVAPI: Cannot register API group " | ||
186 | "%lx with major(%u) minor(%u)\n", | ||
187 | group, major, minor); | ||
188 | prom_halt(); | ||
189 | } | ||
diff --git a/arch/sparc64/kernel/setup.c b/arch/sparc64/kernel/setup.c index 451028341c75..dea9c3c9ec5f 100644 --- a/arch/sparc64/kernel/setup.c +++ b/arch/sparc64/kernel/setup.c | |||
@@ -269,6 +269,7 @@ void __init per_cpu_patch(void) | |||
269 | 269 | ||
270 | void __init sun4v_patch(void) | 270 | void __init sun4v_patch(void) |
271 | { | 271 | { |
272 | extern void sun4v_hvapi_init(void); | ||
272 | struct sun4v_1insn_patch_entry *p1; | 273 | struct sun4v_1insn_patch_entry *p1; |
273 | struct sun4v_2insn_patch_entry *p2; | 274 | struct sun4v_2insn_patch_entry *p2; |
274 | 275 | ||
@@ -300,6 +301,8 @@ void __init sun4v_patch(void) | |||
300 | 301 | ||
301 | p2++; | 302 | p2++; |
302 | } | 303 | } |
304 | |||
305 | sun4v_hvapi_init(); | ||
303 | } | 306 | } |
304 | 307 | ||
305 | #ifdef CONFIG_SMP | 308 | #ifdef CONFIG_SMP |
diff --git a/drivers/serial/sunhv.c b/drivers/serial/sunhv.c index c3a6bd2e7950..96557e6dba60 100644 --- a/drivers/serial/sunhv.c +++ b/drivers/serial/sunhv.c | |||
@@ -1,6 +1,6 @@ | |||
1 | /* sunhv.c: Serial driver for SUN4V hypervisor console. | 1 | /* sunhv.c: Serial driver for SUN4V hypervisor console. |
2 | * | 2 | * |
3 | * Copyright (C) 2006 David S. Miller (davem@davemloft.net) | 3 | * Copyright (C) 2006, 2007 David S. Miller (davem@davemloft.net) |
4 | */ | 4 | */ |
5 | 5 | ||
6 | #include <linux/module.h> | 6 | #include <linux/module.h> |
@@ -35,57 +35,51 @@ | |||
35 | #define CON_BREAK ((long)-1) | 35 | #define CON_BREAK ((long)-1) |
36 | #define CON_HUP ((long)-2) | 36 | #define CON_HUP ((long)-2) |
37 | 37 | ||
38 | static inline long hypervisor_con_getchar(long *status) | 38 | #define IGNORE_BREAK 0x1 |
39 | { | 39 | #define IGNORE_ALL 0x2 |
40 | register unsigned long func asm("%o5"); | ||
41 | register unsigned long arg0 asm("%o0"); | ||
42 | register unsigned long arg1 asm("%o1"); | ||
43 | |||
44 | func = HV_FAST_CONS_GETCHAR; | ||
45 | arg0 = 0; | ||
46 | arg1 = 0; | ||
47 | __asm__ __volatile__("ta %6" | ||
48 | : "=&r" (func), "=&r" (arg0), "=&r" (arg1) | ||
49 | : "0" (func), "1" (arg0), "2" (arg1), | ||
50 | "i" (HV_FAST_TRAP)); | ||
51 | 40 | ||
52 | *status = arg0; | 41 | static char *con_write_page; |
42 | static char *con_read_page; | ||
53 | 43 | ||
54 | return (long) arg1; | 44 | static int hung_up = 0; |
55 | } | ||
56 | 45 | ||
57 | static inline long hypervisor_con_putchar(long ch) | 46 | static void transmit_chars_putchar(struct uart_port *port, struct circ_buf *xmit) |
58 | { | 47 | { |
59 | register unsigned long func asm("%o5"); | 48 | while (!uart_circ_empty(xmit)) { |
60 | register unsigned long arg0 asm("%o0"); | 49 | long status = sun4v_con_putchar(xmit->buf[xmit->tail]); |
61 | 50 | ||
62 | func = HV_FAST_CONS_PUTCHAR; | 51 | if (status != HV_EOK) |
63 | arg0 = ch; | 52 | break; |
64 | __asm__ __volatile__("ta %4" | ||
65 | : "=&r" (func), "=&r" (arg0) | ||
66 | : "0" (func), "1" (arg0), "i" (HV_FAST_TRAP)); | ||
67 | 53 | ||
68 | return (long) arg0; | 54 | xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); |
55 | port->icount.tx++; | ||
56 | } | ||
69 | } | 57 | } |
70 | 58 | ||
71 | #define IGNORE_BREAK 0x1 | 59 | static void transmit_chars_write(struct uart_port *port, struct circ_buf *xmit) |
72 | #define IGNORE_ALL 0x2 | 60 | { |
61 | while (!uart_circ_empty(xmit)) { | ||
62 | unsigned long ra = __pa(xmit->buf + xmit->tail); | ||
63 | unsigned long len, status, sent; | ||
73 | 64 | ||
74 | static int hung_up = 0; | 65 | len = CIRC_CNT_TO_END(xmit->head, xmit->tail, |
66 | UART_XMIT_SIZE); | ||
67 | status = sun4v_con_write(ra, len, &sent); | ||
68 | if (status != HV_EOK) | ||
69 | break; | ||
70 | xmit->tail = (xmit->tail + sent) & (UART_XMIT_SIZE - 1); | ||
71 | port->icount.tx += sent; | ||
72 | } | ||
73 | } | ||
75 | 74 | ||
76 | static struct tty_struct *receive_chars(struct uart_port *port) | 75 | static int receive_chars_getchar(struct uart_port *port, struct tty_struct *tty) |
77 | { | 76 | { |
78 | struct tty_struct *tty = NULL; | ||
79 | int saw_console_brk = 0; | 77 | int saw_console_brk = 0; |
80 | int limit = 10000; | 78 | int limit = 10000; |
81 | 79 | ||
82 | if (port->info != NULL) /* Unopened serial console */ | ||
83 | tty = port->info->tty; | ||
84 | |||
85 | while (limit-- > 0) { | 80 | while (limit-- > 0) { |
86 | long status; | 81 | long status; |
87 | long c = hypervisor_con_getchar(&status); | 82 | long c = sun4v_con_getchar(&status); |
88 | unsigned char flag; | ||
89 | 83 | ||
90 | if (status == HV_EWOULDBLOCK) | 84 | if (status == HV_EWOULDBLOCK) |
91 | break; | 85 | break; |
@@ -110,27 +104,90 @@ static struct tty_struct *receive_chars(struct uart_port *port) | |||
110 | continue; | 104 | continue; |
111 | } | 105 | } |
112 | 106 | ||
113 | flag = TTY_NORMAL; | ||
114 | port->icount.rx++; | 107 | port->icount.rx++; |
115 | if (c == CON_BREAK) { | ||
116 | port->icount.brk++; | ||
117 | if (uart_handle_break(port)) | ||
118 | continue; | ||
119 | flag = TTY_BREAK; | ||
120 | } | ||
121 | 108 | ||
122 | if (uart_handle_sysrq_char(port, c)) | 109 | if (uart_handle_sysrq_char(port, c)) |
123 | continue; | 110 | continue; |
124 | 111 | ||
125 | if ((port->ignore_status_mask & IGNORE_ALL) || | 112 | tty_insert_flip_char(tty, c, TTY_NORMAL); |
126 | ((port->ignore_status_mask & IGNORE_BREAK) && | 113 | } |
127 | (c == CON_BREAK))) | 114 | |
115 | return saw_console_brk; | ||
116 | } | ||
117 | |||
118 | static int receive_chars_read(struct uart_port *port, struct tty_struct *tty) | ||
119 | { | ||
120 | int saw_console_brk = 0; | ||
121 | int limit = 10000; | ||
122 | |||
123 | while (limit-- > 0) { | ||
124 | unsigned long ra = __pa(con_read_page); | ||
125 | unsigned long bytes_read, i; | ||
126 | long stat = sun4v_con_read(ra, PAGE_SIZE, &bytes_read); | ||
127 | |||
128 | if (stat != HV_EOK) { | ||
129 | bytes_read = 0; | ||
130 | |||
131 | if (stat == CON_BREAK) { | ||
132 | if (uart_handle_break(port)) | ||
133 | continue; | ||
134 | saw_console_brk = 1; | ||
135 | *con_read_page = 0; | ||
136 | bytes_read = 1; | ||
137 | } else if (stat == CON_HUP) { | ||
138 | hung_up = 1; | ||
139 | uart_handle_dcd_change(port, 0); | ||
140 | continue; | ||
141 | } else { | ||
142 | /* HV_EWOULDBLOCK, etc. */ | ||
143 | break; | ||
144 | } | ||
145 | } | ||
146 | |||
147 | if (hung_up) { | ||
148 | hung_up = 0; | ||
149 | uart_handle_dcd_change(port, 1); | ||
150 | } | ||
151 | |||
152 | for (i = 0; i < bytes_read; i++) | ||
153 | uart_handle_sysrq_char(port, con_read_page[i]); | ||
154 | |||
155 | if (tty == NULL) | ||
128 | continue; | 156 | continue; |
129 | 157 | ||
130 | tty_insert_flip_char(tty, c, flag); | 158 | port->icount.rx += bytes_read; |
159 | |||
160 | tty_insert_flip_string(tty, con_read_page, bytes_read); | ||
131 | } | 161 | } |
132 | 162 | ||
133 | if (saw_console_brk) | 163 | return saw_console_brk; |
164 | } | ||
165 | |||
166 | struct sunhv_ops { | ||
167 | void (*transmit_chars)(struct uart_port *port, struct circ_buf *xmit); | ||
168 | int (*receive_chars)(struct uart_port *port, struct tty_struct *tty); | ||
169 | }; | ||
170 | |||
171 | static struct sunhv_ops bychar_ops = { | ||
172 | .transmit_chars = transmit_chars_putchar, | ||
173 | .receive_chars = receive_chars_getchar, | ||
174 | }; | ||
175 | |||
176 | static struct sunhv_ops bywrite_ops = { | ||
177 | .transmit_chars = transmit_chars_write, | ||
178 | .receive_chars = receive_chars_read, | ||
179 | }; | ||
180 | |||
181 | static struct sunhv_ops *sunhv_ops = &bychar_ops; | ||
182 | |||
183 | static struct tty_struct *receive_chars(struct uart_port *port) | ||
184 | { | ||
185 | struct tty_struct *tty = NULL; | ||
186 | |||
187 | if (port->info != NULL) /* Unopened serial console */ | ||
188 | tty = port->info->tty; | ||
189 | |||
190 | if (sunhv_ops->receive_chars(port, tty)) | ||
134 | sun_do_break(); | 191 | sun_do_break(); |
135 | 192 | ||
136 | return tty; | 193 | return tty; |
@@ -147,15 +204,7 @@ static void transmit_chars(struct uart_port *port) | |||
147 | if (uart_circ_empty(xmit) || uart_tx_stopped(port)) | 204 | if (uart_circ_empty(xmit) || uart_tx_stopped(port)) |
148 | return; | 205 | return; |
149 | 206 | ||
150 | while (!uart_circ_empty(xmit)) { | 207 | sunhv_ops->transmit_chars(port, xmit); |
151 | long status = hypervisor_con_putchar(xmit->buf[xmit->tail]); | ||
152 | |||
153 | if (status != HV_EOK) | ||
154 | break; | ||
155 | |||
156 | xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); | ||
157 | port->icount.tx++; | ||
158 | } | ||
159 | 208 | ||
160 | if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) | 209 | if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) |
161 | uart_write_wakeup(port); | 210 | uart_write_wakeup(port); |
@@ -212,7 +261,7 @@ static void sunhv_start_tx(struct uart_port *port) | |||
212 | struct circ_buf *xmit = &port->info->xmit; | 261 | struct circ_buf *xmit = &port->info->xmit; |
213 | 262 | ||
214 | while (!uart_circ_empty(xmit)) { | 263 | while (!uart_circ_empty(xmit)) { |
215 | long status = hypervisor_con_putchar(xmit->buf[xmit->tail]); | 264 | long status = sun4v_con_putchar(xmit->buf[xmit->tail]); |
216 | 265 | ||
217 | if (status != HV_EOK) | 266 | if (status != HV_EOK) |
218 | break; | 267 | break; |
@@ -231,9 +280,10 @@ static void sunhv_send_xchar(struct uart_port *port, char ch) | |||
231 | spin_lock_irqsave(&port->lock, flags); | 280 | spin_lock_irqsave(&port->lock, flags); |
232 | 281 | ||
233 | while (limit-- > 0) { | 282 | while (limit-- > 0) { |
234 | long status = hypervisor_con_putchar(ch); | 283 | long status = sun4v_con_putchar(ch); |
235 | if (status == HV_EOK) | 284 | if (status == HV_EOK) |
236 | break; | 285 | break; |
286 | udelay(1); | ||
237 | } | 287 | } |
238 | 288 | ||
239 | spin_unlock_irqrestore(&port->lock, flags); | 289 | spin_unlock_irqrestore(&port->lock, flags); |
@@ -254,15 +304,15 @@ static void sunhv_break_ctl(struct uart_port *port, int break_state) | |||
254 | { | 304 | { |
255 | if (break_state) { | 305 | if (break_state) { |
256 | unsigned long flags; | 306 | unsigned long flags; |
257 | int limit = 1000000; | 307 | int limit = 10000; |
258 | 308 | ||
259 | spin_lock_irqsave(&port->lock, flags); | 309 | spin_lock_irqsave(&port->lock, flags); |
260 | 310 | ||
261 | while (limit-- > 0) { | 311 | while (limit-- > 0) { |
262 | long status = hypervisor_con_putchar(CON_BREAK); | 312 | long status = sun4v_con_putchar(CON_BREAK); |
263 | if (status == HV_EOK) | 313 | if (status == HV_EOK) |
264 | break; | 314 | break; |
265 | udelay(2); | 315 | udelay(1); |
266 | } | 316 | } |
267 | 317 | ||
268 | spin_unlock_irqrestore(&port->lock, flags); | 318 | spin_unlock_irqrestore(&port->lock, flags); |
@@ -359,38 +409,99 @@ static struct uart_driver sunhv_reg = { | |||
359 | 409 | ||
360 | static struct uart_port *sunhv_port; | 410 | static struct uart_port *sunhv_port; |
361 | 411 | ||
362 | static inline void sunhv_console_putchar(struct uart_port *port, char c) | 412 | /* Copy 's' into the con_write_page, decoding "\n" into |
413 | * "\r\n" along the way. We have to return two lengths | ||
414 | * because the caller needs to know how much to advance | ||
415 | * 's' and also how many bytes to output via con_write_page. | ||
416 | */ | ||
417 | static int fill_con_write_page(const char *s, unsigned int n, | ||
418 | unsigned long *page_bytes) | ||
419 | { | ||
420 | const char *orig_s = s; | ||
421 | char *p = con_write_page; | ||
422 | int left = PAGE_SIZE; | ||
423 | |||
424 | while (n--) { | ||
425 | if (*s == '\n') { | ||
426 | if (left < 2) | ||
427 | break; | ||
428 | *p++ = '\r'; | ||
429 | left--; | ||
430 | } else if (left < 1) | ||
431 | break; | ||
432 | *p++ = *s++; | ||
433 | left--; | ||
434 | } | ||
435 | *page_bytes = p - con_write_page; | ||
436 | return s - orig_s; | ||
437 | } | ||
438 | |||
439 | static void sunhv_console_write_paged(struct console *con, const char *s, unsigned n) | ||
363 | { | 440 | { |
441 | struct uart_port *port = sunhv_port; | ||
364 | unsigned long flags; | 442 | unsigned long flags; |
365 | int limit = 1000000; | ||
366 | 443 | ||
367 | spin_lock_irqsave(&port->lock, flags); | 444 | spin_lock_irqsave(&port->lock, flags); |
445 | while (n > 0) { | ||
446 | unsigned long ra = __pa(con_write_page); | ||
447 | unsigned long page_bytes; | ||
448 | unsigned int cpy = fill_con_write_page(s, n, | ||
449 | &page_bytes); | ||
450 | |||
451 | n -= cpy; | ||
452 | s += cpy; | ||
453 | while (page_bytes > 0) { | ||
454 | unsigned long written; | ||
455 | int limit = 1000000; | ||
456 | |||
457 | while (limit--) { | ||
458 | unsigned long stat; | ||
459 | |||
460 | stat = sun4v_con_write(ra, page_bytes, | ||
461 | &written); | ||
462 | if (stat == HV_EOK) | ||
463 | break; | ||
464 | udelay(1); | ||
465 | } | ||
466 | if (limit <= 0) | ||
467 | break; | ||
468 | page_bytes -= written; | ||
469 | ra += written; | ||
470 | } | ||
471 | } | ||
472 | spin_unlock_irqrestore(&port->lock, flags); | ||
473 | } | ||
474 | |||
475 | static inline void sunhv_console_putchar(struct uart_port *port, char c) | ||
476 | { | ||
477 | int limit = 1000000; | ||
368 | 478 | ||
369 | while (limit-- > 0) { | 479 | while (limit-- > 0) { |
370 | long status = hypervisor_con_putchar(c); | 480 | long status = sun4v_con_putchar(c); |
371 | if (status == HV_EOK) | 481 | if (status == HV_EOK) |
372 | break; | 482 | break; |
373 | udelay(2); | 483 | udelay(1); |
374 | } | 484 | } |
375 | |||
376 | spin_unlock_irqrestore(&port->lock, flags); | ||
377 | } | 485 | } |
378 | 486 | ||
379 | static void sunhv_console_write(struct console *con, const char *s, unsigned n) | 487 | static void sunhv_console_write_bychar(struct console *con, const char *s, unsigned n) |
380 | { | 488 | { |
381 | struct uart_port *port = sunhv_port; | 489 | struct uart_port *port = sunhv_port; |
490 | unsigned long flags; | ||
382 | int i; | 491 | int i; |
383 | 492 | ||
493 | spin_lock_irqsave(&port->lock, flags); | ||
384 | for (i = 0; i < n; i++) { | 494 | for (i = 0; i < n; i++) { |
385 | if (*s == '\n') | 495 | if (*s == '\n') |
386 | sunhv_console_putchar(port, '\r'); | 496 | sunhv_console_putchar(port, '\r'); |
387 | sunhv_console_putchar(port, *s++); | 497 | sunhv_console_putchar(port, *s++); |
388 | } | 498 | } |
499 | spin_unlock_irqrestore(&port->lock, flags); | ||
389 | } | 500 | } |
390 | 501 | ||
391 | static struct console sunhv_console = { | 502 | static struct console sunhv_console = { |
392 | .name = "ttyHV", | 503 | .name = "ttyHV", |
393 | .write = sunhv_console_write, | 504 | .write = sunhv_console_write_bychar, |
394 | .device = uart_console_device, | 505 | .device = uart_console_device, |
395 | .flags = CON_PRINTBUFFER, | 506 | .flags = CON_PRINTBUFFER, |
396 | .index = -1, | 507 | .index = -1, |
@@ -410,6 +521,7 @@ static inline struct console *SUNHV_CONSOLE(void) | |||
410 | static int __devinit hv_probe(struct of_device *op, const struct of_device_id *match) | 521 | static int __devinit hv_probe(struct of_device *op, const struct of_device_id *match) |
411 | { | 522 | { |
412 | struct uart_port *port; | 523 | struct uart_port *port; |
524 | unsigned long minor; | ||
413 | int err; | 525 | int err; |
414 | 526 | ||
415 | if (op->irqs[0] == 0xffffffff) | 527 | if (op->irqs[0] == 0xffffffff) |
@@ -419,6 +531,22 @@ static int __devinit hv_probe(struct of_device *op, const struct of_device_id *m | |||
419 | if (unlikely(!port)) | 531 | if (unlikely(!port)) |
420 | return -ENOMEM; | 532 | return -ENOMEM; |
421 | 533 | ||
534 | minor = 1; | ||
535 | if (sun4v_hvapi_register(HV_GRP_CORE, 1, &minor) == 0 && | ||
536 | minor >= 1) { | ||
537 | err = -ENOMEM; | ||
538 | con_write_page = kzalloc(PAGE_SIZE, GFP_KERNEL); | ||
539 | if (!con_write_page) | ||
540 | goto out_free_port; | ||
541 | |||
542 | con_read_page = kzalloc(PAGE_SIZE, GFP_KERNEL); | ||
543 | if (!con_read_page) | ||
544 | goto out_free_con_write_page; | ||
545 | |||
546 | sunhv_console.write = sunhv_console_write_paged; | ||
547 | sunhv_ops = &bywrite_ops; | ||
548 | } | ||
549 | |||
422 | sunhv_port = port; | 550 | sunhv_port = port; |
423 | 551 | ||
424 | port->line = 0; | 552 | port->line = 0; |
@@ -437,7 +565,7 @@ static int __devinit hv_probe(struct of_device *op, const struct of_device_id *m | |||
437 | 565 | ||
438 | err = uart_register_driver(&sunhv_reg); | 566 | err = uart_register_driver(&sunhv_reg); |
439 | if (err) | 567 | if (err) |
440 | goto out_free_port; | 568 | goto out_free_con_read_page; |
441 | 569 | ||
442 | sunhv_reg.tty_driver->name_base = sunhv_reg.minor - 64; | 570 | sunhv_reg.tty_driver->name_base = sunhv_reg.minor - 64; |
443 | sunserial_current_minor += 1; | 571 | sunserial_current_minor += 1; |
@@ -463,6 +591,12 @@ out_unregister_driver: | |||
463 | sunserial_current_minor -= 1; | 591 | sunserial_current_minor -= 1; |
464 | uart_unregister_driver(&sunhv_reg); | 592 | uart_unregister_driver(&sunhv_reg); |
465 | 593 | ||
594 | out_free_con_read_page: | ||
595 | kfree(con_read_page); | ||
596 | |||
597 | out_free_con_write_page: | ||
598 | kfree(con_write_page); | ||
599 | |||
466 | out_free_port: | 600 | out_free_port: |
467 | kfree(port); | 601 | kfree(port); |
468 | sunhv_port = NULL; | 602 | sunhv_port = NULL; |
diff --git a/include/asm-sparc64/hypervisor.h b/include/asm-sparc64/hypervisor.h index 612bf319753f..a5558c87556d 100644 --- a/include/asm-sparc64/hypervisor.h +++ b/include/asm-sparc64/hypervisor.h | |||
@@ -940,6 +940,54 @@ struct hv_fault_status { | |||
940 | */ | 940 | */ |
941 | #define HV_FAST_CONS_PUTCHAR 0x61 | 941 | #define HV_FAST_CONS_PUTCHAR 0x61 |
942 | 942 | ||
943 | /* con_read() | ||
944 | * TRAP: HV_FAST_TRAP | ||
945 | * FUNCTION: HV_FAST_CONS_READ | ||
946 | * ARG0: buffer real address | ||
947 | * ARG1: buffer size in bytes | ||
948 | * RET0: status | ||
949 | * RET1: bytes read or BREAK or HUP | ||
950 | * ERRORS: EWOULDBLOCK No character available. | ||
951 | * | ||
952 | * Reads characters into a buffer from the console device. If no | ||
953 | * character is available then an EWOULDBLOCK error is returned. | ||
954 | * If a character is available, then the returned status is EOK | ||
955 | * and the number of bytes read into the given buffer is provided | ||
956 | * in RET1. | ||
957 | * | ||
958 | * A virtual BREAK is represented by the 64-bit RET1 value -1. | ||
959 | * | ||
960 | * A virtual HUP signal is represented by the 64-bit RET1 value -2. | ||
961 | * | ||
962 | * If BREAK or HUP are indicated, no bytes were read into buffer. | ||
963 | */ | ||
964 | #define HV_FAST_CONS_READ 0x62 | ||
965 | |||
966 | /* con_write() | ||
967 | * TRAP: HV_FAST_TRAP | ||
968 | * FUNCTION: HV_FAST_CONS_WRITE | ||
969 | * ARG0: buffer real address | ||
970 | * ARG1: buffer size in bytes | ||
971 | * RET0: status | ||
972 | * RET1: bytes written | ||
973 | * ERRORS: EWOULDBLOCK Output buffer currently full, would block | ||
974 | * | ||
975 | * Send a characters in buffer to the console device. Breaks must be | ||
976 | * sent using con_putchar(). | ||
977 | */ | ||
978 | #define HV_FAST_CONS_WRITE 0x63 | ||
979 | |||
980 | #ifndef __ASSEMBLY__ | ||
981 | extern long sun4v_con_getchar(long *status); | ||
982 | extern long sun4v_con_putchar(long c); | ||
983 | extern long sun4v_con_read(unsigned long buffer, | ||
984 | unsigned long size, | ||
985 | unsigned long *bytes_read); | ||
986 | extern unsigned long sun4v_con_write(unsigned long buffer, | ||
987 | unsigned long size, | ||
988 | unsigned long *bytes_written); | ||
989 | #endif | ||
990 | |||
943 | /* Trap trace services. | 991 | /* Trap trace services. |
944 | * | 992 | * |
945 | * The hypervisor provides a trap tracing capability for privileged | 993 | * The hypervisor provides a trap tracing capability for privileged |
@@ -2121,8 +2169,41 @@ struct hv_mmu_statistics { | |||
2121 | #define HV_FAST_MMUSTAT_INFO 0x103 | 2169 | #define HV_FAST_MMUSTAT_INFO 0x103 |
2122 | 2170 | ||
2123 | /* Function numbers for HV_CORE_TRAP. */ | 2171 | /* Function numbers for HV_CORE_TRAP. */ |
2124 | #define HV_CORE_VER 0x00 | 2172 | #define HV_CORE_SET_VER 0x00 |
2125 | #define HV_CORE_PUTCHAR 0x01 | 2173 | #define HV_CORE_PUTCHAR 0x01 |
2126 | #define HV_CORE_EXIT 0x02 | 2174 | #define HV_CORE_EXIT 0x02 |
2175 | #define HV_CORE_GET_VER 0x03 | ||
2176 | |||
2177 | /* Hypervisor API groups for use with HV_CORE_SET_VER and | ||
2178 | * HV_CORE_GET_VER. | ||
2179 | */ | ||
2180 | #define HV_GRP_SUN4V 0x0000 | ||
2181 | #define HV_GRP_CORE 0x0001 | ||
2182 | #define HV_GRP_INTR 0x0002 | ||
2183 | #define HV_GRP_SOFT_STATE 0x0003 | ||
2184 | #define HV_GRP_PCI 0x0100 | ||
2185 | #define HV_GRP_LDOM 0x0101 | ||
2186 | #define HV_GRP_SVC_CHAN 0x0102 | ||
2187 | #define HV_GRP_NCS 0x0103 | ||
2188 | #define HV_GRP_NIAG_PERF 0x0200 | ||
2189 | #define HV_GRP_FIRE_PERF 0x0201 | ||
2190 | #define HV_GRP_DIAG 0x0300 | ||
2191 | |||
2192 | #ifndef __ASSEMBLY__ | ||
2193 | extern unsigned long sun4v_get_version(unsigned long group, | ||
2194 | unsigned long *major, | ||
2195 | unsigned long *minor); | ||
2196 | extern unsigned long sun4v_set_version(unsigned long group, | ||
2197 | unsigned long major, | ||
2198 | unsigned long minor, | ||
2199 | unsigned long *actual_minor); | ||
2200 | |||
2201 | extern int sun4v_hvapi_register(unsigned long group, unsigned long major, | ||
2202 | unsigned long *minor); | ||
2203 | extern void sun4v_hvapi_unregister(unsigned long group); | ||
2204 | extern int sun4v_hvapi_get(unsigned long group, | ||
2205 | unsigned long *major, | ||
2206 | unsigned long *minor); | ||
2207 | #endif | ||
2127 | 2208 | ||
2128 | #endif /* !(_SPARC64_HYPERVISOR_H) */ | 2209 | #endif /* !(_SPARC64_HYPERVISOR_H) */ |