diff options
Diffstat (limited to 'drivers/tty/hvc/hvc_vio.c')
-rw-r--r-- | drivers/tty/hvc/hvc_vio.c | 408 |
1 files changed, 360 insertions, 48 deletions
diff --git a/drivers/tty/hvc/hvc_vio.c b/drivers/tty/hvc/hvc_vio.c index e6eea1485244..130aace67f31 100644 --- a/drivers/tty/hvc/hvc_vio.c +++ b/drivers/tty/hvc/hvc_vio.c | |||
@@ -27,15 +27,27 @@ | |||
27 | * You should have received a copy of the GNU General Public License | 27 | * You should have received a copy of the GNU General Public License |
28 | * along with this program; if not, write to the Free Software | 28 | * along with this program; if not, write to the Free Software |
29 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 29 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
30 | * | ||
31 | * TODO: | ||
32 | * | ||
33 | * - handle error in sending hvsi protocol packets | ||
34 | * - retry nego on subsequent sends ? | ||
30 | */ | 35 | */ |
31 | 36 | ||
37 | #undef DEBUG | ||
38 | |||
32 | #include <linux/types.h> | 39 | #include <linux/types.h> |
33 | #include <linux/init.h> | 40 | #include <linux/init.h> |
41 | #include <linux/delay.h> | ||
42 | #include <linux/slab.h> | ||
43 | #include <linux/console.h> | ||
34 | 44 | ||
35 | #include <asm/hvconsole.h> | 45 | #include <asm/hvconsole.h> |
36 | #include <asm/vio.h> | 46 | #include <asm/vio.h> |
37 | #include <asm/prom.h> | 47 | #include <asm/prom.h> |
38 | #include <asm/firmware.h> | 48 | #include <asm/firmware.h> |
49 | #include <asm/hvsi.h> | ||
50 | #include <asm/udbg.h> | ||
39 | 51 | ||
40 | #include "hvc_console.h" | 52 | #include "hvc_console.h" |
41 | 53 | ||
@@ -43,59 +55,236 @@ static const char hvc_driver_name[] = "hvc_console"; | |||
43 | 55 | ||
44 | static struct vio_device_id hvc_driver_table[] __devinitdata = { | 56 | static struct vio_device_id hvc_driver_table[] __devinitdata = { |
45 | {"serial", "hvterm1"}, | 57 | {"serial", "hvterm1"}, |
58 | #ifndef HVC_OLD_HVSI | ||
59 | {"serial", "hvterm-protocol"}, | ||
60 | #endif | ||
46 | { "", "" } | 61 | { "", "" } |
47 | }; | 62 | }; |
48 | MODULE_DEVICE_TABLE(vio, hvc_driver_table); | 63 | MODULE_DEVICE_TABLE(vio, hvc_driver_table); |
49 | 64 | ||
50 | static int filtered_get_chars(uint32_t vtermno, char *buf, int count) | 65 | typedef enum hv_protocol { |
66 | HV_PROTOCOL_RAW, | ||
67 | HV_PROTOCOL_HVSI | ||
68 | } hv_protocol_t; | ||
69 | |||
70 | struct hvterm_priv { | ||
71 | u32 termno; /* HV term number */ | ||
72 | hv_protocol_t proto; /* Raw data or HVSI packets */ | ||
73 | struct hvsi_priv hvsi; /* HVSI specific data */ | ||
74 | spinlock_t buf_lock; | ||
75 | char buf[SIZE_VIO_GET_CHARS]; | ||
76 | int left; | ||
77 | int offset; | ||
78 | }; | ||
79 | static struct hvterm_priv *hvterm_privs[MAX_NR_HVC_CONSOLES]; | ||
80 | /* For early boot console */ | ||
81 | static struct hvterm_priv hvterm_priv0; | ||
82 | |||
83 | static int hvterm_raw_get_chars(uint32_t vtermno, char *buf, int count) | ||
51 | { | 84 | { |
52 | unsigned long got; | 85 | struct hvterm_priv *pv = hvterm_privs[vtermno]; |
53 | int i; | 86 | unsigned long i; |
87 | unsigned long flags; | ||
88 | int got; | ||
54 | 89 | ||
55 | /* | 90 | if (WARN_ON(!pv)) |
56 | * Vio firmware will read up to SIZE_VIO_GET_CHARS at its own discretion | 91 | return 0; |
57 | * so we play safe and avoid the situation where got > count which could | ||
58 | * overload the flip buffer. | ||
59 | */ | ||
60 | if (count < SIZE_VIO_GET_CHARS) | ||
61 | return -EAGAIN; | ||
62 | 92 | ||
63 | got = hvc_get_chars(vtermno, buf, count); | 93 | spin_lock_irqsave(&pv->buf_lock, flags); |
64 | 94 | ||
65 | /* | 95 | if (pv->left == 0) { |
66 | * Work around a HV bug where it gives us a null | 96 | pv->offset = 0; |
67 | * after every \r. -- paulus | 97 | pv->left = hvc_get_chars(pv->termno, pv->buf, count); |
68 | */ | 98 | |
69 | for (i = 1; i < got; ++i) { | 99 | /* |
70 | if (buf[i] == 0 && buf[i-1] == '\r') { | 100 | * Work around a HV bug where it gives us a null |
71 | --got; | 101 | * after every \r. -- paulus |
72 | if (i < got) | 102 | */ |
73 | memmove(&buf[i], &buf[i+1], | 103 | for (i = 1; i < pv->left; ++i) { |
74 | got - i); | 104 | if (pv->buf[i] == 0 && pv->buf[i-1] == '\r') { |
105 | --pv->left; | ||
106 | if (i < pv->left) { | ||
107 | memmove(&pv->buf[i], &pv->buf[i+1], | ||
108 | pv->left - i); | ||
109 | } | ||
110 | } | ||
75 | } | 111 | } |
76 | } | 112 | } |
113 | |||
114 | got = min(count, pv->left); | ||
115 | memcpy(buf, &pv->buf[pv->offset], got); | ||
116 | pv->offset += got; | ||
117 | pv->left -= got; | ||
118 | |||
119 | spin_unlock_irqrestore(&pv->buf_lock, flags); | ||
120 | |||
77 | return got; | 121 | return got; |
78 | } | 122 | } |
79 | 123 | ||
80 | static const struct hv_ops hvc_get_put_ops = { | 124 | static int hvterm_raw_put_chars(uint32_t vtermno, const char *buf, int count) |
81 | .get_chars = filtered_get_chars, | 125 | { |
82 | .put_chars = hvc_put_chars, | 126 | struct hvterm_priv *pv = hvterm_privs[vtermno]; |
127 | |||
128 | if (WARN_ON(!pv)) | ||
129 | return 0; | ||
130 | |||
131 | return hvc_put_chars(pv->termno, buf, count); | ||
132 | } | ||
133 | |||
134 | static const struct hv_ops hvterm_raw_ops = { | ||
135 | .get_chars = hvterm_raw_get_chars, | ||
136 | .put_chars = hvterm_raw_put_chars, | ||
83 | .notifier_add = notifier_add_irq, | 137 | .notifier_add = notifier_add_irq, |
84 | .notifier_del = notifier_del_irq, | 138 | .notifier_del = notifier_del_irq, |
85 | .notifier_hangup = notifier_hangup_irq, | 139 | .notifier_hangup = notifier_hangup_irq, |
86 | }; | 140 | }; |
87 | 141 | ||
142 | static int hvterm_hvsi_get_chars(uint32_t vtermno, char *buf, int count) | ||
143 | { | ||
144 | struct hvterm_priv *pv = hvterm_privs[vtermno]; | ||
145 | |||
146 | if (WARN_ON(!pv)) | ||
147 | return 0; | ||
148 | |||
149 | return hvsilib_get_chars(&pv->hvsi, buf, count); | ||
150 | } | ||
151 | |||
152 | static int hvterm_hvsi_put_chars(uint32_t vtermno, const char *buf, int count) | ||
153 | { | ||
154 | struct hvterm_priv *pv = hvterm_privs[vtermno]; | ||
155 | |||
156 | if (WARN_ON(!pv)) | ||
157 | return 0; | ||
158 | |||
159 | return hvsilib_put_chars(&pv->hvsi, buf, count); | ||
160 | } | ||
161 | |||
162 | static int hvterm_hvsi_open(struct hvc_struct *hp, int data) | ||
163 | { | ||
164 | struct hvterm_priv *pv = hvterm_privs[hp->vtermno]; | ||
165 | int rc; | ||
166 | |||
167 | pr_devel("HVSI@%x: open !\n", pv->termno); | ||
168 | |||
169 | rc = notifier_add_irq(hp, data); | ||
170 | if (rc) | ||
171 | return rc; | ||
172 | |||
173 | return hvsilib_open(&pv->hvsi, hp); | ||
174 | } | ||
175 | |||
176 | static void hvterm_hvsi_close(struct hvc_struct *hp, int data) | ||
177 | { | ||
178 | struct hvterm_priv *pv = hvterm_privs[hp->vtermno]; | ||
179 | |||
180 | pr_devel("HVSI@%x: do close !\n", pv->termno); | ||
181 | |||
182 | hvsilib_close(&pv->hvsi, hp); | ||
183 | |||
184 | notifier_del_irq(hp, data); | ||
185 | } | ||
186 | |||
187 | void hvterm_hvsi_hangup(struct hvc_struct *hp, int data) | ||
188 | { | ||
189 | struct hvterm_priv *pv = hvterm_privs[hp->vtermno]; | ||
190 | |||
191 | pr_devel("HVSI@%x: do hangup !\n", pv->termno); | ||
192 | |||
193 | hvsilib_close(&pv->hvsi, hp); | ||
194 | |||
195 | notifier_hangup_irq(hp, data); | ||
196 | } | ||
197 | |||
198 | static int hvterm_hvsi_tiocmget(struct hvc_struct *hp) | ||
199 | { | ||
200 | struct hvterm_priv *pv = hvterm_privs[hp->vtermno]; | ||
201 | |||
202 | if (!pv) | ||
203 | return -EINVAL; | ||
204 | return pv->hvsi.mctrl; | ||
205 | } | ||
206 | |||
207 | static int hvterm_hvsi_tiocmset(struct hvc_struct *hp, unsigned int set, | ||
208 | unsigned int clear) | ||
209 | { | ||
210 | struct hvterm_priv *pv = hvterm_privs[hp->vtermno]; | ||
211 | |||
212 | pr_devel("HVSI@%x: Set modem control, set=%x,clr=%x\n", | ||
213 | pv->termno, set, clear); | ||
214 | |||
215 | if (set & TIOCM_DTR) | ||
216 | hvsilib_write_mctrl(&pv->hvsi, 1); | ||
217 | else if (clear & TIOCM_DTR) | ||
218 | hvsilib_write_mctrl(&pv->hvsi, 0); | ||
219 | |||
220 | return 0; | ||
221 | } | ||
222 | |||
223 | static const struct hv_ops hvterm_hvsi_ops = { | ||
224 | .get_chars = hvterm_hvsi_get_chars, | ||
225 | .put_chars = hvterm_hvsi_put_chars, | ||
226 | .notifier_add = hvterm_hvsi_open, | ||
227 | .notifier_del = hvterm_hvsi_close, | ||
228 | .notifier_hangup = hvterm_hvsi_hangup, | ||
229 | .tiocmget = hvterm_hvsi_tiocmget, | ||
230 | .tiocmset = hvterm_hvsi_tiocmset, | ||
231 | }; | ||
232 | |||
88 | static int __devinit hvc_vio_probe(struct vio_dev *vdev, | 233 | static int __devinit hvc_vio_probe(struct vio_dev *vdev, |
89 | const struct vio_device_id *id) | 234 | const struct vio_device_id *id) |
90 | { | 235 | { |
236 | const struct hv_ops *ops; | ||
91 | struct hvc_struct *hp; | 237 | struct hvc_struct *hp; |
238 | struct hvterm_priv *pv; | ||
239 | hv_protocol_t proto; | ||
240 | int i, termno = -1; | ||
92 | 241 | ||
93 | /* probed with invalid parameters. */ | 242 | /* probed with invalid parameters. */ |
94 | if (!vdev || !id) | 243 | if (!vdev || !id) |
95 | return -EPERM; | 244 | return -EPERM; |
96 | 245 | ||
97 | hp = hvc_alloc(vdev->unit_address, vdev->irq, &hvc_get_put_ops, | 246 | if (of_device_is_compatible(vdev->dev.of_node, "hvterm1")) { |
98 | MAX_VIO_PUT_CHARS); | 247 | proto = HV_PROTOCOL_RAW; |
248 | ops = &hvterm_raw_ops; | ||
249 | } else if (of_device_is_compatible(vdev->dev.of_node, "hvterm-protocol")) { | ||
250 | proto = HV_PROTOCOL_HVSI; | ||
251 | ops = &hvterm_hvsi_ops; | ||
252 | } else { | ||
253 | pr_err("hvc_vio: Unkown protocol for %s\n", vdev->dev.of_node->full_name); | ||
254 | return -ENXIO; | ||
255 | } | ||
256 | |||
257 | pr_devel("hvc_vio_probe() device %s, using %s protocol\n", | ||
258 | vdev->dev.of_node->full_name, | ||
259 | proto == HV_PROTOCOL_RAW ? "raw" : "hvsi"); | ||
260 | |||
261 | /* Is it our boot one ? */ | ||
262 | if (hvterm_privs[0] == &hvterm_priv0 && | ||
263 | vdev->unit_address == hvterm_priv0.termno) { | ||
264 | pv = hvterm_privs[0]; | ||
265 | termno = 0; | ||
266 | pr_devel("->boot console, using termno 0\n"); | ||
267 | } | ||
268 | /* nope, allocate a new one */ | ||
269 | else { | ||
270 | for (i = 0; i < MAX_NR_HVC_CONSOLES && termno < 0; i++) | ||
271 | if (!hvterm_privs[i]) | ||
272 | termno = i; | ||
273 | pr_devel("->non-boot console, using termno %d\n", termno); | ||
274 | if (termno < 0) | ||
275 | return -ENODEV; | ||
276 | pv = kzalloc(sizeof(struct hvterm_priv), GFP_KERNEL); | ||
277 | if (!pv) | ||
278 | return -ENOMEM; | ||
279 | pv->termno = vdev->unit_address; | ||
280 | pv->proto = proto; | ||
281 | spin_lock_init(&pv->buf_lock); | ||
282 | hvterm_privs[termno] = pv; | ||
283 | hvsilib_init(&pv->hvsi, hvc_get_chars, hvc_put_chars, | ||
284 | pv->termno, 0); | ||
285 | } | ||
286 | |||
287 | hp = hvc_alloc(termno, vdev->irq, ops, MAX_VIO_PUT_CHARS); | ||
99 | if (IS_ERR(hp)) | 288 | if (IS_ERR(hp)) |
100 | return PTR_ERR(hp); | 289 | return PTR_ERR(hp); |
101 | dev_set_drvdata(&vdev->dev, hp); | 290 | dev_set_drvdata(&vdev->dev, hp); |
@@ -106,8 +295,16 @@ static int __devinit hvc_vio_probe(struct vio_dev *vdev, | |||
106 | static int __devexit hvc_vio_remove(struct vio_dev *vdev) | 295 | static int __devexit hvc_vio_remove(struct vio_dev *vdev) |
107 | { | 296 | { |
108 | struct hvc_struct *hp = dev_get_drvdata(&vdev->dev); | 297 | struct hvc_struct *hp = dev_get_drvdata(&vdev->dev); |
298 | int rc, termno; | ||
109 | 299 | ||
110 | return hvc_remove(hp); | 300 | termno = hp->vtermno; |
301 | rc = hvc_remove(hp); | ||
302 | if (rc == 0) { | ||
303 | if (hvterm_privs[termno] != &hvterm_priv0) | ||
304 | kfree(hvterm_privs[termno]); | ||
305 | hvterm_privs[termno] = NULL; | ||
306 | } | ||
307 | return rc; | ||
111 | } | 308 | } |
112 | 309 | ||
113 | static struct vio_driver hvc_vio_driver = { | 310 | static struct vio_driver hvc_vio_driver = { |
@@ -140,34 +337,149 @@ static void __exit hvc_vio_exit(void) | |||
140 | } | 337 | } |
141 | module_exit(hvc_vio_exit); | 338 | module_exit(hvc_vio_exit); |
142 | 339 | ||
143 | /* the device tree order defines our numbering */ | 340 | static void udbg_hvc_putc(char c) |
144 | static int hvc_find_vtys(void) | ||
145 | { | 341 | { |
146 | struct device_node *vty; | 342 | int count = -1; |
147 | int num_found = 0; | ||
148 | 343 | ||
149 | for (vty = of_find_node_by_name(NULL, "vty"); vty != NULL; | 344 | if (c == '\n') |
150 | vty = of_find_node_by_name(vty, "vty")) { | 345 | udbg_hvc_putc('\r'); |
151 | const uint32_t *vtermno; | ||
152 | 346 | ||
153 | /* We have statically defined space for only a certain number | 347 | do { |
154 | * of console adapters. | 348 | switch(hvterm_priv0.proto) { |
155 | */ | 349 | case HV_PROTOCOL_RAW: |
156 | if (num_found >= MAX_NR_HVC_CONSOLES) { | 350 | count = hvterm_raw_put_chars(0, &c, 1); |
157 | of_node_put(vty); | 351 | break; |
352 | case HV_PROTOCOL_HVSI: | ||
353 | count = hvterm_hvsi_put_chars(0, &c, 1); | ||
158 | break; | 354 | break; |
159 | } | 355 | } |
356 | } while(count == 0); | ||
357 | } | ||
358 | |||
359 | static int udbg_hvc_getc_poll(void) | ||
360 | { | ||
361 | int rc = 0; | ||
362 | char c; | ||
160 | 363 | ||
161 | vtermno = of_get_property(vty, "reg", NULL); | 364 | switch(hvterm_priv0.proto) { |
162 | if (!vtermno) | 365 | case HV_PROTOCOL_RAW: |
163 | continue; | 366 | rc = hvterm_raw_get_chars(0, &c, 1); |
367 | break; | ||
368 | case HV_PROTOCOL_HVSI: | ||
369 | rc = hvterm_hvsi_get_chars(0, &c, 1); | ||
370 | break; | ||
371 | } | ||
372 | if (!rc) | ||
373 | return -1; | ||
374 | return c; | ||
375 | } | ||
164 | 376 | ||
165 | if (of_device_is_compatible(vty, "hvterm1")) { | 377 | static int udbg_hvc_getc(void) |
166 | hvc_instantiate(*vtermno, num_found, &hvc_get_put_ops); | 378 | { |
167 | ++num_found; | 379 | int ch; |
380 | for (;;) { | ||
381 | ch = udbg_hvc_getc_poll(); | ||
382 | if (ch == -1) { | ||
383 | /* This shouldn't be needed...but... */ | ||
384 | volatile unsigned long delay; | ||
385 | for (delay=0; delay < 2000000; delay++) | ||
386 | ; | ||
387 | } else { | ||
388 | return ch; | ||
168 | } | 389 | } |
169 | } | 390 | } |
391 | } | ||
392 | |||
393 | void __init hvc_vio_init_early(void) | ||
394 | { | ||
395 | struct device_node *stdout_node; | ||
396 | const u32 *termno; | ||
397 | const char *name; | ||
398 | const struct hv_ops *ops; | ||
399 | |||
400 | /* find the boot console from /chosen/stdout */ | ||
401 | if (!of_chosen) | ||
402 | return; | ||
403 | name = of_get_property(of_chosen, "linux,stdout-path", NULL); | ||
404 | if (name == NULL) | ||
405 | return; | ||
406 | stdout_node = of_find_node_by_path(name); | ||
407 | if (!stdout_node) | ||
408 | return; | ||
409 | name = of_get_property(stdout_node, "name", NULL); | ||
410 | if (!name) { | ||
411 | printk(KERN_WARNING "stdout node missing 'name' property!\n"); | ||
412 | goto out; | ||
413 | } | ||
414 | |||
415 | /* Check if it's a virtual terminal */ | ||
416 | if (strncmp(name, "vty", 3) != 0) | ||
417 | goto out; | ||
418 | termno = of_get_property(stdout_node, "reg", NULL); | ||
419 | if (termno == NULL) | ||
420 | goto out; | ||
421 | hvterm_priv0.termno = *termno; | ||
422 | spin_lock_init(&hvterm_priv0.buf_lock); | ||
423 | hvterm_privs[0] = &hvterm_priv0; | ||
424 | |||
425 | /* Check the protocol */ | ||
426 | if (of_device_is_compatible(stdout_node, "hvterm1")) { | ||
427 | hvterm_priv0.proto = HV_PROTOCOL_RAW; | ||
428 | ops = &hvterm_raw_ops; | ||
429 | } | ||
430 | else if (of_device_is_compatible(stdout_node, "hvterm-protocol")) { | ||
431 | hvterm_priv0.proto = HV_PROTOCOL_HVSI; | ||
432 | ops = &hvterm_hvsi_ops; | ||
433 | hvsilib_init(&hvterm_priv0.hvsi, hvc_get_chars, hvc_put_chars, | ||
434 | hvterm_priv0.termno, 1); | ||
435 | /* HVSI, perform the handshake now */ | ||
436 | hvsilib_establish(&hvterm_priv0.hvsi); | ||
437 | } else | ||
438 | goto out; | ||
439 | udbg_putc = udbg_hvc_putc; | ||
440 | udbg_getc = udbg_hvc_getc; | ||
441 | udbg_getc_poll = udbg_hvc_getc_poll; | ||
442 | #ifdef HVC_OLD_HVSI | ||
443 | /* When using the old HVSI driver don't register the HVC | ||
444 | * backend for HVSI, only do udbg | ||
445 | */ | ||
446 | if (hvterm_priv0.proto == HV_PROTOCOL_HVSI) | ||
447 | goto out; | ||
448 | #endif | ||
449 | add_preferred_console("hvc", 0, NULL); | ||
450 | hvc_instantiate(0, 0, ops); | ||
451 | out: | ||
452 | of_node_put(stdout_node); | ||
453 | } | ||
170 | 454 | ||
171 | return num_found; | 455 | /* call this from early_init() for a working debug console on |
456 | * vterm capable LPAR machines | ||
457 | */ | ||
458 | #ifdef CONFIG_PPC_EARLY_DEBUG_LPAR | ||
459 | void __init udbg_init_debug_lpar(void) | ||
460 | { | ||
461 | hvterm_privs[0] = &hvterm_priv0; | ||
462 | hvterm_priv0.termno = 0; | ||
463 | hvterm_priv0.proto = HV_PROTOCOL_RAW; | ||
464 | spin_lock_init(&hvterm_priv0.buf_lock); | ||
465 | udbg_putc = udbg_hvc_putc; | ||
466 | udbg_getc = udbg_hvc_getc; | ||
467 | udbg_getc_poll = udbg_hvc_getc_poll; | ||
468 | } | ||
469 | #endif /* CONFIG_PPC_EARLY_DEBUG_LPAR */ | ||
470 | |||
471 | #ifdef CONFIG_PPC_EARLY_DEBUG_LPAR_HVSI | ||
472 | void __init udbg_init_debug_lpar_hvsi(void) | ||
473 | { | ||
474 | hvterm_privs[0] = &hvterm_priv0; | ||
475 | hvterm_priv0.termno = CONFIG_PPC_EARLY_DEBUG_HVSI_VTERMNO; | ||
476 | hvterm_priv0.proto = HV_PROTOCOL_HVSI; | ||
477 | spin_lock_init(&hvterm_priv0.buf_lock); | ||
478 | udbg_putc = udbg_hvc_putc; | ||
479 | udbg_getc = udbg_hvc_getc; | ||
480 | udbg_getc_poll = udbg_hvc_getc_poll; | ||
481 | hvsilib_init(&hvterm_priv0.hvsi, hvc_get_chars, hvc_put_chars, | ||
482 | hvterm_priv0.termno, 1); | ||
483 | hvsilib_establish(&hvterm_priv0.hvsi); | ||
172 | } | 484 | } |
173 | console_initcall(hvc_find_vtys); | 485 | #endif /* CONFIG_PPC_EARLY_DEBUG_LPAR_HVSI */ |