Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

tile: various console improvements

This change improves and cleans up the tile console.

- We enable HVC_IRQ support on tilegx, with the addition of a new
Tilera hypervisor API for tilegx to allow a console IPI. If IPI
support is not available we fall back to the previous polling mode.

- We simplify the earlyprintk code to use CON_BOOT and eliminate some
of the other supporting earlyprintk code.

- A new tile_console_write() primitive is used to send output to
the console and is factored out of the hvc_tile driver.
This lets us support a "sim_console" boot argument to allow using
simulator hooks to send output to the "console" as a slightly
faster alternative to emulating the hardware more directly.

Signed-off-by: Chris Metcalf <cmetcalf@tilera.com>
Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

+186 -48
+1
arch/tile/Kconfig
··· 112 112 config HVC_TILE 113 113 depends on TTY 114 114 select HVC_DRIVER 115 + select HVC_IRQ if TILEGX 115 116 def_bool y 116 117 117 118 config TILEGX
+1 -2
arch/tile/include/asm/setup.h
··· 24 24 */ 25 25 #define MAXMEM_PFN PFN_DOWN(MAXMEM) 26 26 27 + int tile_console_write(const char *buf, int count); 27 28 void early_panic(const char *fmt, ...); 28 - void warn_early_printk(void); 29 - void __init disable_early_printk(void); 30 29 31 30 /* Init-time routine to do tile-specific per-cpu setup. */ 32 31 void setup_cpu(int boot);
+28 -1
arch/tile/include/hv/hypervisor.h
··· 318 318 /** hv_set_pte_super_shift */ 319 319 #define HV_DISPATCH_SET_PTE_SUPER_SHIFT 57 320 320 321 + /** hv_console_set_ipi */ 322 + #define HV_DISPATCH_CONSOLE_SET_IPI 63 323 + 321 324 /** One more than the largest dispatch value */ 322 - #define _HV_DISPATCH_END 58 325 + #define _HV_DISPATCH_END 64 323 326 324 327 325 328 #ifndef __ASSEMBLER__ ··· 587 584 * @result Zero if no error, non-zero for invalid parameters. 588 585 */ 589 586 int hv_get_ipi_pte(HV_Coord tile, int pl, HV_PTE* pte); 587 + 588 + /** Configure the console interrupt. 589 + * 590 + * When the console client interrupt is enabled, the hypervisor will 591 + * deliver the specified IPI to the client in the following situations: 592 + * 593 + * - The console has at least one character available for input. 594 + * 595 + * - The console can accept new characters for output, and the last call 596 + * to hv_console_write() did not write all of the characters requested 597 + * by the client. 598 + * 599 + * Note that in some system configurations, console interrupt will not 600 + * be available; clients should be prepared for this routine to fail and 601 + * to fall back to periodic console polling in that case. 602 + * 603 + * @param ipi Index of the IPI register which will receive the interrupt. 604 + * @param event IPI event number for console interrupt. If less than 0, 605 + * disable the console IPI interrupt. 606 + * @param coord Tile to be targeted for console interrupt. 607 + * @return 0 on success, otherwise, HV_EINVAL if illegal parameter, 608 + * HV_ENOTSUP if console interrupt are not available. 609 + */ 610 + int hv_console_set_ipi(int ipi, int event, HV_Coord coord); 590 611 591 612 #else /* !CHIP_HAS_IPI() */ 592 613
+11 -36
arch/tile/kernel/early_printk.c
··· 23 23 24 24 static void early_hv_write(struct console *con, const char *s, unsigned n) 25 25 { 26 - hv_console_write((HV_VirtAddr) s, n); 26 + tile_console_write(s, n); 27 + 28 + /* 29 + * Convert NL to NLCR (close enough to CRNL) during early boot. 30 + * We assume newlines are at the ends of strings, which turns out 31 + * to be good enough for early boot console output. 32 + */ 33 + if (n && s[n-1] == '\n') 34 + tile_console_write("\r", 1); 27 35 } 28 36 29 37 static struct console early_hv_console = { 30 38 .name = "earlyhv", 31 39 .write = early_hv_write, 32 - .flags = CON_PRINTBUFFER, 40 + .flags = CON_PRINTBUFFER | CON_BOOT, 33 41 .index = -1, 34 42 }; 35 - 36 - /* Direct interface for emergencies */ 37 - static int early_console_complete; 38 43 39 44 void early_panic(const char *fmt, ...) 40 45 { ··· 48 43 va_start(ap, fmt); 49 44 early_printk("Kernel panic - not syncing: "); 50 45 early_vprintk(fmt, ap); 51 - early_console->write(early_console, "\n", 1); 46 + early_printk("\n"); 52 47 va_end(ap); 53 48 dump_stack(); 54 49 hv_halt(); 55 50 } 56 - 57 - static int __initdata keep_early; 58 51 59 52 static int __init setup_early_printk(char *str) 60 53 { 61 54 if (early_console) 62 55 return 1; 63 56 64 - if (str != NULL && strncmp(str, "keep", 4) == 0) 65 - keep_early = 1; 66 - 67 57 early_console = &early_hv_console; 68 58 register_console(early_console); 69 59 70 60 return 0; 71 - } 72 - 73 - void __init disable_early_printk(void) 74 - { 75 - early_console_complete = 1; 76 - if (!early_console) 77 - return; 78 - if (!keep_early) { 79 - early_printk("disabling early console\n"); 80 - unregister_console(early_console); 81 - early_console = NULL; 82 - } else { 83 - early_printk("keeping early console\n"); 84 - } 85 - } 86 - 87 - void warn_early_printk(void) 88 - { 89 - if (early_console_complete || early_console) 90 - return; 91 - early_printk("\ 92 - Machine shutting down before console output is fully initialized.\n\ 93 - You may wish to reboot and add the option 'earlyprintk' to your\n\ 94 - boot command line to see any diagnostic early console output.\n\ 95 - "); 96 61 } 97 62 98 63 early_param("earlyprintk", setup_early_printk);
+2 -1
arch/tile/kernel/hvglue.lds
··· 56 56 hv_flush_all = TEXT_OFFSET + 0x106e0; 57 57 hv_get_ipi_pte = TEXT_OFFSET + 0x10700; 58 58 hv_set_pte_super_shift = TEXT_OFFSET + 0x10720; 59 - hv_glue_internals = TEXT_OFFSET + 0x10740; 59 + hv_console_set_ipi = TEXT_OFFSET + 0x107e0; 60 + hv_glue_internals = TEXT_OFFSET + 0x10800;
-2
arch/tile/kernel/reboot.c
··· 27 27 28 28 void machine_halt(void) 29 29 { 30 - warn_early_printk(); 31 30 arch_local_irq_disable_all(); 32 31 smp_send_stop(); 33 32 hv_halt(); ··· 34 35 35 36 void machine_power_off(void) 36 37 { 37 - warn_early_printk(); 38 38 arch_local_irq_disable_all(); 39 39 smp_send_stop(); 40 40 hv_power_off();
+143 -6
drivers/tty/hvc/hvc_tile.c
··· 18 18 #include <linux/delay.h> 19 19 #include <linux/err.h> 20 20 #include <linux/init.h> 21 + #include <linux/interrupt.h> 22 + #include <linux/irq.h> 21 23 #include <linux/moduleparam.h> 24 + #include <linux/platform_device.h> 22 25 #include <linux/types.h> 26 + 27 + #include <asm/setup.h> 28 + #include <arch/sim_def.h> 23 29 24 30 #include <hv/hypervisor.h> 25 31 26 32 #include "hvc_console.h" 27 33 34 + static int use_sim_console; 35 + static int __init sim_console(char *str) 36 + { 37 + use_sim_console = 1; 38 + return 0; 39 + } 40 + early_param("sim_console", sim_console); 41 + 42 + int tile_console_write(const char *buf, int count) 43 + { 44 + if (unlikely(use_sim_console)) { 45 + int i; 46 + for (i = 0; i < count; ++i) 47 + __insn_mtspr(SPR_SIM_CONTROL, SIM_CONTROL_PUTC | 48 + (buf[i] << _SIM_CONTROL_OPERATOR_BITS)); 49 + __insn_mtspr(SPR_SIM_CONTROL, SIM_CONTROL_PUTC | 50 + (SIM_PUTC_FLUSH_BINARY << 51 + _SIM_CONTROL_OPERATOR_BITS)); 52 + return 0; 53 + } else { 54 + return hv_console_write((HV_VirtAddr)buf, count); 55 + } 56 + } 57 + 28 58 static int hvc_tile_put_chars(uint32_t vt, const char *buf, int count) 29 59 { 30 - return hv_console_write((HV_VirtAddr)buf, count); 60 + return tile_console_write(buf, count); 31 61 } 32 62 33 63 static int hvc_tile_get_chars(uint32_t vt, char *buf, int count) ··· 74 44 return i; 75 45 } 76 46 47 + #ifdef __tilegx__ 48 + /* 49 + * IRQ based callbacks. 50 + */ 51 + static int hvc_tile_notifier_add_irq(struct hvc_struct *hp, int irq) 52 + { 53 + int rc; 54 + int cpu = raw_smp_processor_id(); /* Choose an arbitrary cpu */ 55 + HV_Coord coord = { .x = cpu_x(cpu), .y = cpu_y(cpu) }; 56 + 57 + rc = notifier_add_irq(hp, irq); 58 + if (rc) 59 + return rc; 60 + 61 + /* 62 + * Request that the hypervisor start sending us interrupts. 63 + * If the hypervisor returns an error, we still return 0, so that 64 + * we can fall back to polling. 65 + */ 66 + if (hv_console_set_ipi(KERNEL_PL, irq, coord) < 0) 67 + notifier_del_irq(hp, irq); 68 + 69 + return 0; 70 + } 71 + 72 + static void hvc_tile_notifier_del_irq(struct hvc_struct *hp, int irq) 73 + { 74 + HV_Coord coord = { 0, 0 }; 75 + 76 + /* Tell the hypervisor to stop sending us interrupts. */ 77 + hv_console_set_ipi(KERNEL_PL, -1, coord); 78 + 79 + notifier_del_irq(hp, irq); 80 + } 81 + 82 + static void hvc_tile_notifier_hangup_irq(struct hvc_struct *hp, int irq) 83 + { 84 + hvc_tile_notifier_del_irq(hp, irq); 85 + } 86 + #endif 87 + 77 88 static const struct hv_ops hvc_tile_get_put_ops = { 78 89 .get_chars = hvc_tile_get_chars, 79 90 .put_chars = hvc_tile_put_chars, 91 + #ifdef __tilegx__ 92 + .notifier_add = hvc_tile_notifier_add_irq, 93 + .notifier_del = hvc_tile_notifier_del_irq, 94 + .notifier_hangup = hvc_tile_notifier_hangup_irq, 95 + #endif 80 96 }; 97 + 98 + 99 + #ifdef __tilegx__ 100 + static int hvc_tile_probe(struct platform_device *pdev) 101 + { 102 + struct hvc_struct *hp; 103 + int tile_hvc_irq; 104 + 105 + /* Create our IRQ and register it. */ 106 + tile_hvc_irq = create_irq(); 107 + if (tile_hvc_irq < 0) 108 + return -ENXIO; 109 + 110 + tile_irq_activate(tile_hvc_irq, TILE_IRQ_PERCPU); 111 + hp = hvc_alloc(0, tile_hvc_irq, &hvc_tile_get_put_ops, 128); 112 + if (IS_ERR(hp)) { 113 + destroy_irq(tile_hvc_irq); 114 + return PTR_ERR(hp); 115 + } 116 + dev_set_drvdata(&pdev->dev, hp); 117 + 118 + return 0; 119 + } 120 + 121 + static int hvc_tile_remove(struct platform_device *pdev) 122 + { 123 + int rc; 124 + struct hvc_struct *hp = dev_get_drvdata(&pdev->dev); 125 + 126 + rc = hvc_remove(hp); 127 + if (rc == 0) 128 + destroy_irq(hp->data); 129 + 130 + return rc; 131 + } 132 + 133 + static void hvc_tile_shutdown(struct platform_device *pdev) 134 + { 135 + struct hvc_struct *hp = dev_get_drvdata(&pdev->dev); 136 + 137 + hvc_tile_notifier_del_irq(hp, hp->data); 138 + } 139 + 140 + static struct platform_device hvc_tile_pdev = { 141 + .name = "hvc-tile", 142 + .id = 0, 143 + }; 144 + 145 + static struct platform_driver hvc_tile_driver = { 146 + .probe = hvc_tile_probe, 147 + .remove = hvc_tile_remove, 148 + .shutdown = hvc_tile_shutdown, 149 + .driver = { 150 + .name = "hvc-tile", 151 + .owner = THIS_MODULE, 152 + } 153 + }; 154 + #endif 81 155 82 156 static int __init hvc_tile_console_init(void) 83 157 { 84 - extern void disable_early_printk(void); 85 158 hvc_instantiate(0, 0, &hvc_tile_get_put_ops); 86 159 add_preferred_console("hvc", 0, NULL); 87 - disable_early_printk(); 88 160 return 0; 89 161 } 90 162 console_initcall(hvc_tile_console_init); 91 163 92 164 static int __init hvc_tile_init(void) 93 165 { 94 - struct hvc_struct *s; 95 - s = hvc_alloc(0, 0, &hvc_tile_get_put_ops, 128); 96 - return IS_ERR(s) ? PTR_ERR(s) : 0; 166 + #ifndef __tilegx__ 167 + struct hvc_struct *hp; 168 + hp = hvc_alloc(0, 0, &hvc_tile_get_put_ops, 128); 169 + return IS_ERR(hp) ? PTR_ERR(hp) : 0; 170 + #else 171 + platform_device_register(&hvc_tile_pdev); 172 + return platform_driver_register(&hvc_tile_driver); 173 + #endif 97 174 } 98 175 device_initcall(hvc_tile_init);