Loading arch/sparc64/kernel/Makefile +1 −1 Original line number Diff line number Diff line Loading @@ -12,7 +12,7 @@ obj-y := process.o setup.o cpu.o idprom.o \ irq.o ptrace.o time.o sys_sparc.o signal.o \ unaligned.o central.o pci.o starfire.o semaphore.o \ power.o sbus.o iommu_common.o sparc64_ksyms.o chmc.o \ visemul.o prom.o of_device.o visemul.o prom.o of_device.o hvapi.o obj-$(CONFIG_STACKTRACE) += stacktrace.o obj-$(CONFIG_PCI) += ebus.o isa.o pci_common.o pci_iommu.o \ Loading arch/sparc64/kernel/entry.S +94 −0 Original line number Diff line number Diff line Loading @@ -1843,3 +1843,97 @@ sun4v_cpu_state: mov %o1, %o0 1: retl nop /* %o0: API group number * %o1: pointer to unsigned long major number storage * %o2: pointer to unsigned long minor number storage * * returns %o0: status */ .globl sun4v_get_version sun4v_get_version: mov HV_CORE_GET_VER, %o5 mov %o1, %o3 mov %o2, %o4 ta HV_CORE_TRAP stx %o1, [%o3] retl stx %o2, [%o4] /* %o0: API group number * %o1: desired major number * %o2: desired minor number * %o3: pointer to unsigned long actual minor number storage * * returns %o0: status */ .globl sun4v_set_version sun4v_set_version: mov HV_CORE_SET_VER, %o5 mov %o3, %o4 ta HV_CORE_TRAP retl stx %o1, [%o4] /* %o0: pointer to unsigned long status * * returns %o0: signed character */ .globl sun4v_con_getchar sun4v_con_getchar: mov %o0, %o4 mov HV_FAST_CONS_GETCHAR, %o5 clr %o0 clr %o1 ta HV_FAST_TRAP stx %o0, [%o4] retl sra %o1, 0, %o0 /* %o0: signed long character * * returns %o0: status */ .globl sun4v_con_putchar sun4v_con_putchar: mov HV_FAST_CONS_PUTCHAR, %o5 ta HV_FAST_TRAP retl sra %o0, 0, %o0 /* %o0: buffer real address * %o1: buffer size * %o2: pointer to unsigned long bytes_read * * returns %o0: status */ .globl sun4v_con_read sun4v_con_read: mov %o2, %o4 mov HV_FAST_CONS_READ, %o5 ta HV_FAST_TRAP brnz %o0, 1f cmp %o1, -1 /* break */ be,a,pn %icc, 1f mov %o1, %o0 cmp %o1, -2 /* hup */ be,a,pn %icc, 1f mov %o1, %o0 stx %o1, [%o4] 1: retl nop /* %o0: buffer real address * %o1: buffer size * %o2: pointer to unsigned long bytes_written * * returns %o0: status */ .globl sun4v_con_write sun4v_con_write: mov %o2, %o4 mov HV_FAST_CONS_WRITE, %o5 ta HV_FAST_TRAP stx %o1, [%o4] retl nop arch/sparc64/kernel/hvapi.c 0 → 100644 +189 −0 Original line number Diff line number Diff line /* hvapi.c: Hypervisor API management. * * Copyright (C) 2007 David S. Miller <davem@davemloft.net> */ #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> #include <linux/slab.h> #include <asm/hypervisor.h> #include <asm/oplib.h> /* If the hypervisor indicates that the API setting * calls are unsupported, by returning HV_EBADTRAP or * HV_ENOTSUPPORTED, we assume that API groups with the * PRE_API flag set are major 1 minor 0. */ struct api_info { unsigned long group; unsigned long major; unsigned long minor; unsigned int refcnt; unsigned int flags; #define FLAG_PRE_API 0x00000001 }; static struct api_info api_table[] = { { .group = HV_GRP_SUN4V, .flags = FLAG_PRE_API }, { .group = HV_GRP_CORE, .flags = FLAG_PRE_API }, { .group = HV_GRP_INTR, }, { .group = HV_GRP_SOFT_STATE, }, { .group = HV_GRP_PCI, .flags = FLAG_PRE_API }, { .group = HV_GRP_LDOM, }, { .group = HV_GRP_SVC_CHAN, .flags = FLAG_PRE_API }, { .group = HV_GRP_NCS, .flags = FLAG_PRE_API }, { .group = HV_GRP_NIAG_PERF, .flags = FLAG_PRE_API }, { .group = HV_GRP_FIRE_PERF, }, { .group = HV_GRP_DIAG, .flags = FLAG_PRE_API }, }; static DEFINE_SPINLOCK(hvapi_lock); static struct api_info *__get_info(unsigned long group) { int i; for (i = 0; i < ARRAY_SIZE(api_table); i++) { if (api_table[i].group == group) return &api_table[i]; } return NULL; } static void __get_ref(struct api_info *p) { p->refcnt++; } static void __put_ref(struct api_info *p) { if (--p->refcnt == 0) { unsigned long ignore; sun4v_set_version(p->group, 0, 0, &ignore); p->major = p->minor = 0; } } /* Register a hypervisor API specification. It indicates the * API group and desired major+minor. * * If an existing API registration exists '0' (success) will * be returned if it is compatible with the one being registered. * Otherwise a negative error code will be returned. * * Otherwise an attempt will be made to negotiate the requested * API group/major/minor with the hypervisor, and errors returned * if that does not succeed. */ int sun4v_hvapi_register(unsigned long group, unsigned long major, unsigned long *minor) { struct api_info *p; unsigned long flags; int ret; spin_lock_irqsave(&hvapi_lock, flags); p = __get_info(group); ret = -EINVAL; if (p) { if (p->refcnt) { ret = -EINVAL; if (p->major == major) { *minor = p->minor; ret = 0; } } else { unsigned long actual_minor; unsigned long hv_ret; hv_ret = sun4v_set_version(group, major, *minor, &actual_minor); ret = -EINVAL; if (hv_ret == HV_EOK) { *minor = actual_minor; p->major = major; p->minor = actual_minor; ret = 0; } else if (hv_ret == HV_EBADTRAP || HV_ENOTSUPPORTED) { if (p->flags & FLAG_PRE_API) { if (major == 1) { p->major = 1; p->minor = 0; *minor = 0; ret = 0; } } } } if (ret == 0) __get_ref(p); } spin_unlock_irqrestore(&hvapi_lock, flags); return ret; } EXPORT_SYMBOL(sun4v_hvapi_register); void sun4v_hvapi_unregister(unsigned long group) { struct api_info *p; unsigned long flags; spin_lock_irqsave(&hvapi_lock, flags); p = __get_info(group); if (p) __put_ref(p); spin_unlock_irqrestore(&hvapi_lock, flags); } EXPORT_SYMBOL(sun4v_hvapi_unregister); int sun4v_hvapi_get(unsigned long group, unsigned long *major, unsigned long *minor) { struct api_info *p; unsigned long flags; int ret; spin_lock_irqsave(&hvapi_lock, flags); ret = -EINVAL; p = __get_info(group); if (p && p->refcnt) { *major = p->major; *minor = p->minor; ret = 0; } spin_unlock_irqrestore(&hvapi_lock, flags); return ret; } EXPORT_SYMBOL(sun4v_hvapi_get); void __init sun4v_hvapi_init(void) { unsigned long group, major, minor; group = HV_GRP_SUN4V; major = 1; minor = 0; if (sun4v_hvapi_register(group, major, &minor)) goto bad; group = HV_GRP_CORE; major = 1; minor = 1; if (sun4v_hvapi_register(group, major, &minor)) goto bad; return; bad: prom_printf("HVAPI: Cannot register API group " "%lx with major(%u) minor(%u)\n", group, major, minor); prom_halt(); } arch/sparc64/kernel/setup.c +3 −0 Original line number Diff line number Diff line Loading @@ -269,6 +269,7 @@ void __init per_cpu_patch(void) void __init sun4v_patch(void) { extern void sun4v_hvapi_init(void); struct sun4v_1insn_patch_entry *p1; struct sun4v_2insn_patch_entry *p2; Loading Loading @@ -300,6 +301,8 @@ void __init sun4v_patch(void) p2++; } sun4v_hvapi_init(); } #ifdef CONFIG_SMP Loading drivers/serial/sunhv.c +205 −71 Original line number Diff line number Diff line /* sunhv.c: Serial driver for SUN4V hypervisor console. * * Copyright (C) 2006 David S. Miller (davem@davemloft.net) * Copyright (C) 2006, 2007 David S. Miller (davem@davemloft.net) */ #include <linux/module.h> Loading Loading @@ -35,57 +35,51 @@ #define CON_BREAK ((long)-1) #define CON_HUP ((long)-2) static inline long hypervisor_con_getchar(long *status) { register unsigned long func asm("%o5"); register unsigned long arg0 asm("%o0"); register unsigned long arg1 asm("%o1"); func = HV_FAST_CONS_GETCHAR; arg0 = 0; arg1 = 0; __asm__ __volatile__("ta %6" : "=&r" (func), "=&r" (arg0), "=&r" (arg1) : "0" (func), "1" (arg0), "2" (arg1), "i" (HV_FAST_TRAP)); #define IGNORE_BREAK 0x1 #define IGNORE_ALL 0x2 *status = arg0; static char *con_write_page; static char *con_read_page; return (long) arg1; } static int hung_up = 0; static inline long hypervisor_con_putchar(long ch) static void transmit_chars_putchar(struct uart_port *port, struct circ_buf *xmit) { register unsigned long func asm("%o5"); register unsigned long arg0 asm("%o0"); while (!uart_circ_empty(xmit)) { long status = sun4v_con_putchar(xmit->buf[xmit->tail]); func = HV_FAST_CONS_PUTCHAR; arg0 = ch; __asm__ __volatile__("ta %4" : "=&r" (func), "=&r" (arg0) : "0" (func), "1" (arg0), "i" (HV_FAST_TRAP)); if (status != HV_EOK) break; return (long) arg0; xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); port->icount.tx++; } } #define IGNORE_BREAK 0x1 #define IGNORE_ALL 0x2 static void transmit_chars_write(struct uart_port *port, struct circ_buf *xmit) { while (!uart_circ_empty(xmit)) { unsigned long ra = __pa(xmit->buf + xmit->tail); unsigned long len, status, sent; static int hung_up = 0; len = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); status = sun4v_con_write(ra, len, &sent); if (status != HV_EOK) break; xmit->tail = (xmit->tail + sent) & (UART_XMIT_SIZE - 1); port->icount.tx += sent; } } static struct tty_struct *receive_chars(struct uart_port *port) static int receive_chars_getchar(struct uart_port *port, struct tty_struct *tty) { struct tty_struct *tty = NULL; int saw_console_brk = 0; int limit = 10000; if (port->info != NULL) /* Unopened serial console */ tty = port->info->tty; while (limit-- > 0) { long status; long c = hypervisor_con_getchar(&status); unsigned char flag; long c = sun4v_con_getchar(&status); if (status == HV_EWOULDBLOCK) break; Loading @@ -110,27 +104,90 @@ static struct tty_struct *receive_chars(struct uart_port *port) continue; } flag = TTY_NORMAL; port->icount.rx++; if (c == CON_BREAK) { port->icount.brk++; if (uart_handle_break(port)) if (uart_handle_sysrq_char(port, c)) continue; flag = TTY_BREAK; tty_insert_flip_char(tty, c, TTY_NORMAL); } if (uart_handle_sysrq_char(port, c)) return saw_console_brk; } static int receive_chars_read(struct uart_port *port, struct tty_struct *tty) { int saw_console_brk = 0; int limit = 10000; while (limit-- > 0) { unsigned long ra = __pa(con_read_page); unsigned long bytes_read, i; long stat = sun4v_con_read(ra, PAGE_SIZE, &bytes_read); if (stat != HV_EOK) { bytes_read = 0; if (stat == CON_BREAK) { if (uart_handle_break(port)) continue; saw_console_brk = 1; *con_read_page = 0; bytes_read = 1; } else if (stat == CON_HUP) { hung_up = 1; uart_handle_dcd_change(port, 0); continue; } else { /* HV_EWOULDBLOCK, etc. */ break; } } if (hung_up) { hung_up = 0; uart_handle_dcd_change(port, 1); } if ((port->ignore_status_mask & IGNORE_ALL) || ((port->ignore_status_mask & IGNORE_BREAK) && (c == CON_BREAK))) for (i = 0; i < bytes_read; i++) uart_handle_sysrq_char(port, con_read_page[i]); if (tty == NULL) continue; tty_insert_flip_char(tty, c, flag); port->icount.rx += bytes_read; tty_insert_flip_string(tty, con_read_page, bytes_read); } return saw_console_brk; } if (saw_console_brk) struct sunhv_ops { void (*transmit_chars)(struct uart_port *port, struct circ_buf *xmit); int (*receive_chars)(struct uart_port *port, struct tty_struct *tty); }; static struct sunhv_ops bychar_ops = { .transmit_chars = transmit_chars_putchar, .receive_chars = receive_chars_getchar, }; static struct sunhv_ops bywrite_ops = { .transmit_chars = transmit_chars_write, .receive_chars = receive_chars_read, }; static struct sunhv_ops *sunhv_ops = &bychar_ops; static struct tty_struct *receive_chars(struct uart_port *port) { struct tty_struct *tty = NULL; if (port->info != NULL) /* Unopened serial console */ tty = port->info->tty; if (sunhv_ops->receive_chars(port, tty)) sun_do_break(); return tty; Loading @@ -147,15 +204,7 @@ static void transmit_chars(struct uart_port *port) if (uart_circ_empty(xmit) || uart_tx_stopped(port)) return; while (!uart_circ_empty(xmit)) { long status = hypervisor_con_putchar(xmit->buf[xmit->tail]); if (status != HV_EOK) break; xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); port->icount.tx++; } sunhv_ops->transmit_chars(port, xmit); if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(port); Loading Loading @@ -212,7 +261,7 @@ static void sunhv_start_tx(struct uart_port *port) struct circ_buf *xmit = &port->info->xmit; while (!uart_circ_empty(xmit)) { long status = hypervisor_con_putchar(xmit->buf[xmit->tail]); long status = sun4v_con_putchar(xmit->buf[xmit->tail]); if (status != HV_EOK) break; Loading @@ -231,9 +280,10 @@ static void sunhv_send_xchar(struct uart_port *port, char ch) spin_lock_irqsave(&port->lock, flags); while (limit-- > 0) { long status = hypervisor_con_putchar(ch); long status = sun4v_con_putchar(ch); if (status == HV_EOK) break; udelay(1); } spin_unlock_irqrestore(&port->lock, flags); Loading @@ -254,15 +304,15 @@ static void sunhv_break_ctl(struct uart_port *port, int break_state) { if (break_state) { unsigned long flags; int limit = 1000000; int limit = 10000; spin_lock_irqsave(&port->lock, flags); while (limit-- > 0) { long status = hypervisor_con_putchar(CON_BREAK); long status = sun4v_con_putchar(CON_BREAK); if (status == HV_EOK) break; udelay(2); udelay(1); } spin_unlock_irqrestore(&port->lock, flags); Loading Loading @@ -359,38 +409,99 @@ static struct uart_driver sunhv_reg = { static struct uart_port *sunhv_port; static inline void sunhv_console_putchar(struct uart_port *port, char c) /* Copy 's' into the con_write_page, decoding "\n" into * "\r\n" along the way. We have to return two lengths * because the caller needs to know how much to advance * 's' and also how many bytes to output via con_write_page. */ static int fill_con_write_page(const char *s, unsigned int n, unsigned long *page_bytes) { const char *orig_s = s; char *p = con_write_page; int left = PAGE_SIZE; while (n--) { if (*s == '\n') { if (left < 2) break; *p++ = '\r'; left--; } else if (left < 1) break; *p++ = *s++; left--; } *page_bytes = p - con_write_page; return s - orig_s; } static void sunhv_console_write_paged(struct console *con, const char *s, unsigned n) { struct uart_port *port = sunhv_port; unsigned long flags; int limit = 1000000; spin_lock_irqsave(&port->lock, flags); while (n > 0) { unsigned long ra = __pa(con_write_page); unsigned long page_bytes; unsigned int cpy = fill_con_write_page(s, n, &page_bytes); n -= cpy; s += cpy; while (page_bytes > 0) { unsigned long written; int limit = 1000000; while (limit--) { unsigned long stat; stat = sun4v_con_write(ra, page_bytes, &written); if (stat == HV_EOK) break; udelay(1); } if (limit <= 0) break; page_bytes -= written; ra += written; } } spin_unlock_irqrestore(&port->lock, flags); } static inline void sunhv_console_putchar(struct uart_port *port, char c) { int limit = 1000000; while (limit-- > 0) { long status = hypervisor_con_putchar(c); long status = sun4v_con_putchar(c); if (status == HV_EOK) break; udelay(2); udelay(1); } spin_unlock_irqrestore(&port->lock, flags); } static void sunhv_console_write(struct console *con, const char *s, unsigned n) static void sunhv_console_write_bychar(struct console *con, const char *s, unsigned n) { struct uart_port *port = sunhv_port; unsigned long flags; int i; spin_lock_irqsave(&port->lock, flags); for (i = 0; i < n; i++) { if (*s == '\n') sunhv_console_putchar(port, '\r'); sunhv_console_putchar(port, *s++); } spin_unlock_irqrestore(&port->lock, flags); } static struct console sunhv_console = { .name = "ttyHV", .write = sunhv_console_write, .write = sunhv_console_write_bychar, .device = uart_console_device, .flags = CON_PRINTBUFFER, .index = -1, Loading @@ -410,6 +521,7 @@ static inline struct console *SUNHV_CONSOLE(void) static int __devinit hv_probe(struct of_device *op, const struct of_device_id *match) { struct uart_port *port; unsigned long minor; int err; if (op->irqs[0] == 0xffffffff) Loading @@ -419,6 +531,22 @@ static int __devinit hv_probe(struct of_device *op, const struct of_device_id *m if (unlikely(!port)) return -ENOMEM; minor = 1; if (sun4v_hvapi_register(HV_GRP_CORE, 1, &minor) == 0 && minor >= 1) { err = -ENOMEM; con_write_page = kzalloc(PAGE_SIZE, GFP_KERNEL); if (!con_write_page) goto out_free_port; con_read_page = kzalloc(PAGE_SIZE, GFP_KERNEL); if (!con_read_page) goto out_free_con_write_page; sunhv_console.write = sunhv_console_write_paged; sunhv_ops = &bywrite_ops; } sunhv_port = port; port->line = 0; Loading @@ -437,7 +565,7 @@ static int __devinit hv_probe(struct of_device *op, const struct of_device_id *m err = uart_register_driver(&sunhv_reg); if (err) goto out_free_port; goto out_free_con_read_page; sunhv_reg.tty_driver->name_base = sunhv_reg.minor - 64; sunserial_current_minor += 1; Loading @@ -463,6 +591,12 @@ static int __devinit hv_probe(struct of_device *op, const struct of_device_id *m sunserial_current_minor -= 1; uart_unregister_driver(&sunhv_reg); out_free_con_read_page: kfree(con_read_page); out_free_con_write_page: kfree(con_write_page); out_free_port: kfree(port); sunhv_port = NULL; Loading Loading
arch/sparc64/kernel/Makefile +1 −1 Original line number Diff line number Diff line Loading @@ -12,7 +12,7 @@ obj-y := process.o setup.o cpu.o idprom.o \ irq.o ptrace.o time.o sys_sparc.o signal.o \ unaligned.o central.o pci.o starfire.o semaphore.o \ power.o sbus.o iommu_common.o sparc64_ksyms.o chmc.o \ visemul.o prom.o of_device.o visemul.o prom.o of_device.o hvapi.o obj-$(CONFIG_STACKTRACE) += stacktrace.o obj-$(CONFIG_PCI) += ebus.o isa.o pci_common.o pci_iommu.o \ Loading
arch/sparc64/kernel/entry.S +94 −0 Original line number Diff line number Diff line Loading @@ -1843,3 +1843,97 @@ sun4v_cpu_state: mov %o1, %o0 1: retl nop /* %o0: API group number * %o1: pointer to unsigned long major number storage * %o2: pointer to unsigned long minor number storage * * returns %o0: status */ .globl sun4v_get_version sun4v_get_version: mov HV_CORE_GET_VER, %o5 mov %o1, %o3 mov %o2, %o4 ta HV_CORE_TRAP stx %o1, [%o3] retl stx %o2, [%o4] /* %o0: API group number * %o1: desired major number * %o2: desired minor number * %o3: pointer to unsigned long actual minor number storage * * returns %o0: status */ .globl sun4v_set_version sun4v_set_version: mov HV_CORE_SET_VER, %o5 mov %o3, %o4 ta HV_CORE_TRAP retl stx %o1, [%o4] /* %o0: pointer to unsigned long status * * returns %o0: signed character */ .globl sun4v_con_getchar sun4v_con_getchar: mov %o0, %o4 mov HV_FAST_CONS_GETCHAR, %o5 clr %o0 clr %o1 ta HV_FAST_TRAP stx %o0, [%o4] retl sra %o1, 0, %o0 /* %o0: signed long character * * returns %o0: status */ .globl sun4v_con_putchar sun4v_con_putchar: mov HV_FAST_CONS_PUTCHAR, %o5 ta HV_FAST_TRAP retl sra %o0, 0, %o0 /* %o0: buffer real address * %o1: buffer size * %o2: pointer to unsigned long bytes_read * * returns %o0: status */ .globl sun4v_con_read sun4v_con_read: mov %o2, %o4 mov HV_FAST_CONS_READ, %o5 ta HV_FAST_TRAP brnz %o0, 1f cmp %o1, -1 /* break */ be,a,pn %icc, 1f mov %o1, %o0 cmp %o1, -2 /* hup */ be,a,pn %icc, 1f mov %o1, %o0 stx %o1, [%o4] 1: retl nop /* %o0: buffer real address * %o1: buffer size * %o2: pointer to unsigned long bytes_written * * returns %o0: status */ .globl sun4v_con_write sun4v_con_write: mov %o2, %o4 mov HV_FAST_CONS_WRITE, %o5 ta HV_FAST_TRAP stx %o1, [%o4] retl nop
arch/sparc64/kernel/hvapi.c 0 → 100644 +189 −0 Original line number Diff line number Diff line /* hvapi.c: Hypervisor API management. * * Copyright (C) 2007 David S. Miller <davem@davemloft.net> */ #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> #include <linux/slab.h> #include <asm/hypervisor.h> #include <asm/oplib.h> /* If the hypervisor indicates that the API setting * calls are unsupported, by returning HV_EBADTRAP or * HV_ENOTSUPPORTED, we assume that API groups with the * PRE_API flag set are major 1 minor 0. */ struct api_info { unsigned long group; unsigned long major; unsigned long minor; unsigned int refcnt; unsigned int flags; #define FLAG_PRE_API 0x00000001 }; static struct api_info api_table[] = { { .group = HV_GRP_SUN4V, .flags = FLAG_PRE_API }, { .group = HV_GRP_CORE, .flags = FLAG_PRE_API }, { .group = HV_GRP_INTR, }, { .group = HV_GRP_SOFT_STATE, }, { .group = HV_GRP_PCI, .flags = FLAG_PRE_API }, { .group = HV_GRP_LDOM, }, { .group = HV_GRP_SVC_CHAN, .flags = FLAG_PRE_API }, { .group = HV_GRP_NCS, .flags = FLAG_PRE_API }, { .group = HV_GRP_NIAG_PERF, .flags = FLAG_PRE_API }, { .group = HV_GRP_FIRE_PERF, }, { .group = HV_GRP_DIAG, .flags = FLAG_PRE_API }, }; static DEFINE_SPINLOCK(hvapi_lock); static struct api_info *__get_info(unsigned long group) { int i; for (i = 0; i < ARRAY_SIZE(api_table); i++) { if (api_table[i].group == group) return &api_table[i]; } return NULL; } static void __get_ref(struct api_info *p) { p->refcnt++; } static void __put_ref(struct api_info *p) { if (--p->refcnt == 0) { unsigned long ignore; sun4v_set_version(p->group, 0, 0, &ignore); p->major = p->minor = 0; } } /* Register a hypervisor API specification. It indicates the * API group and desired major+minor. * * If an existing API registration exists '0' (success) will * be returned if it is compatible with the one being registered. * Otherwise a negative error code will be returned. * * Otherwise an attempt will be made to negotiate the requested * API group/major/minor with the hypervisor, and errors returned * if that does not succeed. */ int sun4v_hvapi_register(unsigned long group, unsigned long major, unsigned long *minor) { struct api_info *p; unsigned long flags; int ret; spin_lock_irqsave(&hvapi_lock, flags); p = __get_info(group); ret = -EINVAL; if (p) { if (p->refcnt) { ret = -EINVAL; if (p->major == major) { *minor = p->minor; ret = 0; } } else { unsigned long actual_minor; unsigned long hv_ret; hv_ret = sun4v_set_version(group, major, *minor, &actual_minor); ret = -EINVAL; if (hv_ret == HV_EOK) { *minor = actual_minor; p->major = major; p->minor = actual_minor; ret = 0; } else if (hv_ret == HV_EBADTRAP || HV_ENOTSUPPORTED) { if (p->flags & FLAG_PRE_API) { if (major == 1) { p->major = 1; p->minor = 0; *minor = 0; ret = 0; } } } } if (ret == 0) __get_ref(p); } spin_unlock_irqrestore(&hvapi_lock, flags); return ret; } EXPORT_SYMBOL(sun4v_hvapi_register); void sun4v_hvapi_unregister(unsigned long group) { struct api_info *p; unsigned long flags; spin_lock_irqsave(&hvapi_lock, flags); p = __get_info(group); if (p) __put_ref(p); spin_unlock_irqrestore(&hvapi_lock, flags); } EXPORT_SYMBOL(sun4v_hvapi_unregister); int sun4v_hvapi_get(unsigned long group, unsigned long *major, unsigned long *minor) { struct api_info *p; unsigned long flags; int ret; spin_lock_irqsave(&hvapi_lock, flags); ret = -EINVAL; p = __get_info(group); if (p && p->refcnt) { *major = p->major; *minor = p->minor; ret = 0; } spin_unlock_irqrestore(&hvapi_lock, flags); return ret; } EXPORT_SYMBOL(sun4v_hvapi_get); void __init sun4v_hvapi_init(void) { unsigned long group, major, minor; group = HV_GRP_SUN4V; major = 1; minor = 0; if (sun4v_hvapi_register(group, major, &minor)) goto bad; group = HV_GRP_CORE; major = 1; minor = 1; if (sun4v_hvapi_register(group, major, &minor)) goto bad; return; bad: prom_printf("HVAPI: Cannot register API group " "%lx with major(%u) minor(%u)\n", group, major, minor); prom_halt(); }
arch/sparc64/kernel/setup.c +3 −0 Original line number Diff line number Diff line Loading @@ -269,6 +269,7 @@ void __init per_cpu_patch(void) void __init sun4v_patch(void) { extern void sun4v_hvapi_init(void); struct sun4v_1insn_patch_entry *p1; struct sun4v_2insn_patch_entry *p2; Loading Loading @@ -300,6 +301,8 @@ void __init sun4v_patch(void) p2++; } sun4v_hvapi_init(); } #ifdef CONFIG_SMP Loading
drivers/serial/sunhv.c +205 −71 Original line number Diff line number Diff line /* sunhv.c: Serial driver for SUN4V hypervisor console. * * Copyright (C) 2006 David S. Miller (davem@davemloft.net) * Copyright (C) 2006, 2007 David S. Miller (davem@davemloft.net) */ #include <linux/module.h> Loading Loading @@ -35,57 +35,51 @@ #define CON_BREAK ((long)-1) #define CON_HUP ((long)-2) static inline long hypervisor_con_getchar(long *status) { register unsigned long func asm("%o5"); register unsigned long arg0 asm("%o0"); register unsigned long arg1 asm("%o1"); func = HV_FAST_CONS_GETCHAR; arg0 = 0; arg1 = 0; __asm__ __volatile__("ta %6" : "=&r" (func), "=&r" (arg0), "=&r" (arg1) : "0" (func), "1" (arg0), "2" (arg1), "i" (HV_FAST_TRAP)); #define IGNORE_BREAK 0x1 #define IGNORE_ALL 0x2 *status = arg0; static char *con_write_page; static char *con_read_page; return (long) arg1; } static int hung_up = 0; static inline long hypervisor_con_putchar(long ch) static void transmit_chars_putchar(struct uart_port *port, struct circ_buf *xmit) { register unsigned long func asm("%o5"); register unsigned long arg0 asm("%o0"); while (!uart_circ_empty(xmit)) { long status = sun4v_con_putchar(xmit->buf[xmit->tail]); func = HV_FAST_CONS_PUTCHAR; arg0 = ch; __asm__ __volatile__("ta %4" : "=&r" (func), "=&r" (arg0) : "0" (func), "1" (arg0), "i" (HV_FAST_TRAP)); if (status != HV_EOK) break; return (long) arg0; xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); port->icount.tx++; } } #define IGNORE_BREAK 0x1 #define IGNORE_ALL 0x2 static void transmit_chars_write(struct uart_port *port, struct circ_buf *xmit) { while (!uart_circ_empty(xmit)) { unsigned long ra = __pa(xmit->buf + xmit->tail); unsigned long len, status, sent; static int hung_up = 0; len = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); status = sun4v_con_write(ra, len, &sent); if (status != HV_EOK) break; xmit->tail = (xmit->tail + sent) & (UART_XMIT_SIZE - 1); port->icount.tx += sent; } } static struct tty_struct *receive_chars(struct uart_port *port) static int receive_chars_getchar(struct uart_port *port, struct tty_struct *tty) { struct tty_struct *tty = NULL; int saw_console_brk = 0; int limit = 10000; if (port->info != NULL) /* Unopened serial console */ tty = port->info->tty; while (limit-- > 0) { long status; long c = hypervisor_con_getchar(&status); unsigned char flag; long c = sun4v_con_getchar(&status); if (status == HV_EWOULDBLOCK) break; Loading @@ -110,27 +104,90 @@ static struct tty_struct *receive_chars(struct uart_port *port) continue; } flag = TTY_NORMAL; port->icount.rx++; if (c == CON_BREAK) { port->icount.brk++; if (uart_handle_break(port)) if (uart_handle_sysrq_char(port, c)) continue; flag = TTY_BREAK; tty_insert_flip_char(tty, c, TTY_NORMAL); } if (uart_handle_sysrq_char(port, c)) return saw_console_brk; } static int receive_chars_read(struct uart_port *port, struct tty_struct *tty) { int saw_console_brk = 0; int limit = 10000; while (limit-- > 0) { unsigned long ra = __pa(con_read_page); unsigned long bytes_read, i; long stat = sun4v_con_read(ra, PAGE_SIZE, &bytes_read); if (stat != HV_EOK) { bytes_read = 0; if (stat == CON_BREAK) { if (uart_handle_break(port)) continue; saw_console_brk = 1; *con_read_page = 0; bytes_read = 1; } else if (stat == CON_HUP) { hung_up = 1; uart_handle_dcd_change(port, 0); continue; } else { /* HV_EWOULDBLOCK, etc. */ break; } } if (hung_up) { hung_up = 0; uart_handle_dcd_change(port, 1); } if ((port->ignore_status_mask & IGNORE_ALL) || ((port->ignore_status_mask & IGNORE_BREAK) && (c == CON_BREAK))) for (i = 0; i < bytes_read; i++) uart_handle_sysrq_char(port, con_read_page[i]); if (tty == NULL) continue; tty_insert_flip_char(tty, c, flag); port->icount.rx += bytes_read; tty_insert_flip_string(tty, con_read_page, bytes_read); } return saw_console_brk; } if (saw_console_brk) struct sunhv_ops { void (*transmit_chars)(struct uart_port *port, struct circ_buf *xmit); int (*receive_chars)(struct uart_port *port, struct tty_struct *tty); }; static struct sunhv_ops bychar_ops = { .transmit_chars = transmit_chars_putchar, .receive_chars = receive_chars_getchar, }; static struct sunhv_ops bywrite_ops = { .transmit_chars = transmit_chars_write, .receive_chars = receive_chars_read, }; static struct sunhv_ops *sunhv_ops = &bychar_ops; static struct tty_struct *receive_chars(struct uart_port *port) { struct tty_struct *tty = NULL; if (port->info != NULL) /* Unopened serial console */ tty = port->info->tty; if (sunhv_ops->receive_chars(port, tty)) sun_do_break(); return tty; Loading @@ -147,15 +204,7 @@ static void transmit_chars(struct uart_port *port) if (uart_circ_empty(xmit) || uart_tx_stopped(port)) return; while (!uart_circ_empty(xmit)) { long status = hypervisor_con_putchar(xmit->buf[xmit->tail]); if (status != HV_EOK) break; xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); port->icount.tx++; } sunhv_ops->transmit_chars(port, xmit); if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(port); Loading Loading @@ -212,7 +261,7 @@ static void sunhv_start_tx(struct uart_port *port) struct circ_buf *xmit = &port->info->xmit; while (!uart_circ_empty(xmit)) { long status = hypervisor_con_putchar(xmit->buf[xmit->tail]); long status = sun4v_con_putchar(xmit->buf[xmit->tail]); if (status != HV_EOK) break; Loading @@ -231,9 +280,10 @@ static void sunhv_send_xchar(struct uart_port *port, char ch) spin_lock_irqsave(&port->lock, flags); while (limit-- > 0) { long status = hypervisor_con_putchar(ch); long status = sun4v_con_putchar(ch); if (status == HV_EOK) break; udelay(1); } spin_unlock_irqrestore(&port->lock, flags); Loading @@ -254,15 +304,15 @@ static void sunhv_break_ctl(struct uart_port *port, int break_state) { if (break_state) { unsigned long flags; int limit = 1000000; int limit = 10000; spin_lock_irqsave(&port->lock, flags); while (limit-- > 0) { long status = hypervisor_con_putchar(CON_BREAK); long status = sun4v_con_putchar(CON_BREAK); if (status == HV_EOK) break; udelay(2); udelay(1); } spin_unlock_irqrestore(&port->lock, flags); Loading Loading @@ -359,38 +409,99 @@ static struct uart_driver sunhv_reg = { static struct uart_port *sunhv_port; static inline void sunhv_console_putchar(struct uart_port *port, char c) /* Copy 's' into the con_write_page, decoding "\n" into * "\r\n" along the way. We have to return two lengths * because the caller needs to know how much to advance * 's' and also how many bytes to output via con_write_page. */ static int fill_con_write_page(const char *s, unsigned int n, unsigned long *page_bytes) { const char *orig_s = s; char *p = con_write_page; int left = PAGE_SIZE; while (n--) { if (*s == '\n') { if (left < 2) break; *p++ = '\r'; left--; } else if (left < 1) break; *p++ = *s++; left--; } *page_bytes = p - con_write_page; return s - orig_s; } static void sunhv_console_write_paged(struct console *con, const char *s, unsigned n) { struct uart_port *port = sunhv_port; unsigned long flags; int limit = 1000000; spin_lock_irqsave(&port->lock, flags); while (n > 0) { unsigned long ra = __pa(con_write_page); unsigned long page_bytes; unsigned int cpy = fill_con_write_page(s, n, &page_bytes); n -= cpy; s += cpy; while (page_bytes > 0) { unsigned long written; int limit = 1000000; while (limit--) { unsigned long stat; stat = sun4v_con_write(ra, page_bytes, &written); if (stat == HV_EOK) break; udelay(1); } if (limit <= 0) break; page_bytes -= written; ra += written; } } spin_unlock_irqrestore(&port->lock, flags); } static inline void sunhv_console_putchar(struct uart_port *port, char c) { int limit = 1000000; while (limit-- > 0) { long status = hypervisor_con_putchar(c); long status = sun4v_con_putchar(c); if (status == HV_EOK) break; udelay(2); udelay(1); } spin_unlock_irqrestore(&port->lock, flags); } static void sunhv_console_write(struct console *con, const char *s, unsigned n) static void sunhv_console_write_bychar(struct console *con, const char *s, unsigned n) { struct uart_port *port = sunhv_port; unsigned long flags; int i; spin_lock_irqsave(&port->lock, flags); for (i = 0; i < n; i++) { if (*s == '\n') sunhv_console_putchar(port, '\r'); sunhv_console_putchar(port, *s++); } spin_unlock_irqrestore(&port->lock, flags); } static struct console sunhv_console = { .name = "ttyHV", .write = sunhv_console_write, .write = sunhv_console_write_bychar, .device = uart_console_device, .flags = CON_PRINTBUFFER, .index = -1, Loading @@ -410,6 +521,7 @@ static inline struct console *SUNHV_CONSOLE(void) static int __devinit hv_probe(struct of_device *op, const struct of_device_id *match) { struct uart_port *port; unsigned long minor; int err; if (op->irqs[0] == 0xffffffff) Loading @@ -419,6 +531,22 @@ static int __devinit hv_probe(struct of_device *op, const struct of_device_id *m if (unlikely(!port)) return -ENOMEM; minor = 1; if (sun4v_hvapi_register(HV_GRP_CORE, 1, &minor) == 0 && minor >= 1) { err = -ENOMEM; con_write_page = kzalloc(PAGE_SIZE, GFP_KERNEL); if (!con_write_page) goto out_free_port; con_read_page = kzalloc(PAGE_SIZE, GFP_KERNEL); if (!con_read_page) goto out_free_con_write_page; sunhv_console.write = sunhv_console_write_paged; sunhv_ops = &bywrite_ops; } sunhv_port = port; port->line = 0; Loading @@ -437,7 +565,7 @@ static int __devinit hv_probe(struct of_device *op, const struct of_device_id *m err = uart_register_driver(&sunhv_reg); if (err) goto out_free_port; goto out_free_con_read_page; sunhv_reg.tty_driver->name_base = sunhv_reg.minor - 64; sunserial_current_minor += 1; Loading @@ -463,6 +591,12 @@ static int __devinit hv_probe(struct of_device *op, const struct of_device_id *m sunserial_current_minor -= 1; uart_unregister_driver(&sunhv_reg); out_free_con_read_page: kfree(con_read_page); out_free_con_write_page: kfree(con_write_page); out_free_port: kfree(port); sunhv_port = NULL; Loading