initcalls: Add initial implementation
Create simple serial subsystem which makes use of initcalls, and convert existing serial drivers to its use.
This commit is contained in:
		| @@ -3,6 +3,9 @@ SUBDIRS := | ||||
|  | ||||
| include $(BASEDIR)/header.mk | ||||
|  | ||||
| OBJS_$(d) := \ | ||||
| 	$(d)/serial.o | ||||
|  | ||||
| OBJS_$(d)_$(CONFIG_VEXPRESS_A9) := \ | ||||
| 	$(d)/pl011.o \ | ||||
| 	$(d)/pl111.o | ||||
| @@ -15,6 +18,6 @@ OBJS_$(d)_$(CONFIG_RPI) := \ | ||||
| 	$(d)/bcm2835_mailbox.o \ | ||||
| 	$(d)/bcm2835_videocore.o | ||||
|  | ||||
| KOBJS += $(OBJS_$(d)_y) | ||||
| KOBJS += $(OBJS_$(d)) $(OBJS_$(d)_y) | ||||
|  | ||||
| include $(BASEDIR)/footer.mk | ||||
|   | ||||
| @@ -18,8 +18,11 @@ | ||||
|     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
|  */ | ||||
|  | ||||
| #include <init.h> | ||||
| #include <types.h> | ||||
|  | ||||
| #include <drivers/serial.h> | ||||
|  | ||||
| #define VC_MMU_IO_OFFSET(addr) (addr - 0x7E000000 + 0x20000000) | ||||
|  | ||||
| /* Auxiliary IO registers (includes both mini-UART and SPI) */ | ||||
| @@ -57,7 +60,7 @@ | ||||
| #define GPPUD VC_MMU_IO_OFFSET(0x7E200094) | ||||
| #define GPPUDCLK0 VC_MMU_IO_OFFSET(0x7E200098) | ||||
|  | ||||
| void mini_uart_init() { | ||||
| void mini_uart_device_init() { | ||||
| 	uint32 aux_enables; | ||||
| 	uint32 gpfsel1; | ||||
| 	unsigned int i; | ||||
| @@ -96,10 +99,23 @@ void mini_uart_init() { | ||||
| 	*(uint32 *)AUX_MU_CNTL_REG = 2; //enable TX | ||||
| } | ||||
|  | ||||
| void mini_uart_putc(char c) { | ||||
| int mini_uart_putc(char c) { | ||||
| 	/* Wait until the serial buffer is empty */ | ||||
| 	while (!(*(volatile uint32*)AUX_MU_LSR_REG & MINI_UART_TX_EMPTY)); | ||||
|  | ||||
| 	/* When it's empty, write our character */ | ||||
| 	*(volatile uint32*)AUX_MU_IO_REG = c; | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| struct serial_dev mini_uart_dev = { | ||||
| 	.putc = &mini_uart_putc | ||||
| }; | ||||
|  | ||||
| void mini_uart_init() { | ||||
| 	mini_uart_device_init(); | ||||
| 	serial_register_device(&mini_uart_dev); | ||||
| } | ||||
|  | ||||
| early_initcall(mini_uart_init); | ||||
|   | ||||
| @@ -18,17 +18,31 @@ | ||||
|     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
|  */ | ||||
|  | ||||
| #include <init.h> | ||||
| #include <drivers/serial.h> | ||||
| #include <types.h> | ||||
|  | ||||
| #define PL011_SERIAL_BASE 0x10009000 | ||||
| #define PL011_SERIAL_FLAG_REGISTER 0x18 | ||||
| #define PL011_SERIAL_BUFFER_FULL (1 << 5) | ||||
|  | ||||
| void pl011_putc(char c) | ||||
| int pl011_putc(char c) | ||||
| { | ||||
| 	/* Wait until the serial buffer is empty */ | ||||
| 	while (*(volatile uint32*)(PL011_SERIAL_BASE + PL011_SERIAL_FLAG_REGISTER) & (PL011_SERIAL_BUFFER_FULL)); | ||||
|  | ||||
| 	/* When it's empty, put our character at the base */ | ||||
| 	*(volatile uint32*)PL011_SERIAL_BASE = c; | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| struct serial_dev pl011_dev = { | ||||
| 	.putc = &pl011_putc | ||||
| }; | ||||
|  | ||||
| void pl011_init() { | ||||
| 	serial_register_device(&pl011_dev); | ||||
| } | ||||
|  | ||||
| early_initcall(pl011_init); | ||||
|   | ||||
							
								
								
									
										40
									
								
								drivers/serial.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								drivers/serial.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,40 @@ | ||||
| /* | ||||
|     Copyright (C) 2012, Aaron Lindsay <aaron@aclindsay.com> | ||||
|  | ||||
|     This file is part of Aedrix. | ||||
|  | ||||
|     This program is free software; you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation; either version 2 of the License, or | ||||
|     (at your option) any later version. | ||||
|  | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
|  | ||||
|     You should have received a copy of the GNU General Public License along | ||||
|     with this program; if not, write to the Free Software Foundation, Inc., | ||||
|     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
|  */ | ||||
|  | ||||
| #include <init.h> | ||||
| #include <drivers/serial.h> | ||||
|  | ||||
| struct serial_dev *first_serial_dev; | ||||
|  | ||||
| int serial_register_device(struct serial_dev *sdev) { | ||||
| 	if (!first_serial_dev) | ||||
| 		first_serial_dev = sdev; | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| struct serial_dev *serial_first_device() { | ||||
| 	return first_serial_dev; | ||||
| } | ||||
|  | ||||
| void serial_init() { | ||||
| 	first_serial_dev = 0; | ||||
| } | ||||
|  | ||||
| driversubsys_initcall(serial_init); | ||||
| @@ -22,6 +22,6 @@ | ||||
| #define CONSOLE_H | ||||
|  | ||||
| void console_init(struct fb *f); | ||||
| void console_putc(char c); | ||||
| int console_putc(char c); | ||||
|  | ||||
| #endif /* CONSOLE_H */ | ||||
|   | ||||
							
								
								
									
										27
									
								
								include/drivers/serial.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								include/drivers/serial.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| /* | ||||
|     Copyright (C) 2012, Aaron Lindsay <aaron@aclindsay.com> | ||||
|  | ||||
|     This file is part of Aedrix. | ||||
|  | ||||
|     This program is free software; you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation; either version 2 of the License, or | ||||
|     (at your option) any later version. | ||||
|  | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
|  | ||||
|     You should have received a copy of the GNU General Public License along | ||||
|     with this program; if not, write to the Free Software Foundation, Inc., | ||||
|     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
|  */ | ||||
|  | ||||
| struct serial_dev { | ||||
| 	//TODO add more functions and attributes here | ||||
| 	int (*putc)(char); | ||||
| }; | ||||
|  | ||||
| int serial_register_device(struct serial_dev *sdev); | ||||
| struct serial_dev *serial_first_device(); | ||||
							
								
								
									
										34
									
								
								include/init.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								include/init.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| /* | ||||
|     Copyright (C) 2012, Aaron Lindsay <aaron@aclindsay.com> | ||||
|  | ||||
|     This file is part of Aedrix. | ||||
|  | ||||
|     This program is free software; you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation; either version 2 of the License, or | ||||
|     (at your option) any later version. | ||||
|  | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
|  | ||||
|     You should have received a copy of the GNU General Public License along | ||||
|     with this program; if not, write to the Free Software Foundation, Inc., | ||||
|     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
|  */ | ||||
|  | ||||
| void init_earlyinitcalls(); | ||||
| void init_initcalls(); | ||||
|  | ||||
| struct initcall_func { | ||||
| 	void (*fptr)(); | ||||
| }; | ||||
|  | ||||
| #define _initcall(level_name, level, func) struct initcall_func func##_##level##_initcall \ | ||||
| 	__attribute__ ((section ("." level_name "initcalls"))) = { func } | ||||
|  | ||||
| /* The actual calls to be used externally */ | ||||
| #define early_initcall(func) _initcall("early", 0, func) | ||||
| #define driversubsys_initcall(func) _initcall("driversubsys", 1, func) | ||||
| #define device_initcall(func) _initcall("device", 2, func) | ||||
| @@ -21,8 +21,8 @@ | ||||
| #ifndef PRINT_H | ||||
| #define PRINT_H | ||||
|  | ||||
| void print_init(void (*putc)(char)); | ||||
| void print_init(int (*putc)(char)); | ||||
| int print(char *fmt, ...); | ||||
| int print_func(void (putcf)(char), char *fmt, ...); | ||||
| int print_func(int (putcf)(char), char *fmt, ...); | ||||
|  | ||||
| #endif /* PRINT_H */ | ||||
|   | ||||
| @@ -123,17 +123,17 @@ void console_newline() { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void console_putc(char c) { | ||||
| int console_putc(char c) { | ||||
| 	char *console_buffer_end = console_buffer + chars_per_line * lines; | ||||
| 	char *write_char_at; | ||||
|  | ||||
| 	switch (c) { | ||||
| 		case '\n': | ||||
| 			console_newline(); | ||||
| 			return; | ||||
| 			return 0; | ||||
| 		case '\r': | ||||
| 			curr_char = 0; | ||||
| 			return; | ||||
| 			return 0; | ||||
| 		default: | ||||
| 			break; | ||||
| 	} | ||||
| @@ -148,4 +148,6 @@ void console_putc(char c) { | ||||
| 	*write_char_at = c; | ||||
|  | ||||
| 	console_char_to_fb(c, curr_char++, curr_line); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|   | ||||
							
								
								
									
										37
									
								
								kernel/init.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								kernel/init.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | ||||
| /* | ||||
|     Copyright (C) 2012, Aaron Lindsay <aaron@aclindsay.com> | ||||
|  | ||||
|     This file is part of Aedrix. | ||||
|  | ||||
|     This program is free software; you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation; either version 2 of the License, or | ||||
|     (at your option) any later version. | ||||
|  | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
|  | ||||
|     You should have received a copy of the GNU General Public License along | ||||
|     with this program; if not, write to the Free Software Foundation, Inc., | ||||
|     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
|  */ | ||||
|  | ||||
| #include <types.h> | ||||
| #include <print.h> | ||||
|  | ||||
| extern uint32 early_initcalls_start, early_initcalls_end; | ||||
| extern uint32 initcalls_start, initcalls_end; | ||||
|  | ||||
| void init_earlyinitcalls() { | ||||
| 	void (**initcall)(); | ||||
| 	for (initcall = (void (**)()) &early_initcalls_start; initcall < (void (**)())&early_initcalls_end; initcall++) | ||||
| 		(*initcall)(); | ||||
| } | ||||
|  | ||||
| void init_initcalls() { | ||||
| 	void (**initcall)(); | ||||
| 	for (initcall = (void (**)()) &initcalls_start; initcall < (void (**)())&initcalls_end; initcall++) | ||||
| 		(*initcall)(); | ||||
| } | ||||
| @@ -7,6 +7,7 @@ OBJS_$(d) := $(d)/atags.o \ | ||||
| 	$(d)/console.o \ | ||||
| 	$(d)/font.o \ | ||||
| 	$(d)/framebuffer.o \ | ||||
| 	$(d)/init.o \ | ||||
| 	$(d)/kmalloc.o \ | ||||
| 	$(d)/list.o \ | ||||
| 	$(d)/math.o \ | ||||
|   | ||||
| @@ -23,22 +23,23 @@ | ||||
|  | ||||
| /* This function exists solely so crashes don't happen if putc() gets | ||||
|  * called before it is initialized. */ | ||||
| void putc_initial(char c) { | ||||
| int putc_initial(char c) { | ||||
| 	(void)c; | ||||
| 	return 0; | ||||
| } | ||||
| void (*print_putc)(char) = &putc_initial; | ||||
| int (*print_putc)(char) = &putc_initial; | ||||
|  | ||||
| void print_init(void (*putcfn)(char)) { | ||||
| void print_init(int (*putcfn)(char)) { | ||||
| 	if (putcfn) | ||||
| 		print_putc = putcfn; | ||||
| } | ||||
|   | ||||
| void puts(void (*putc)(char), const char *s) | ||||
| void puts(int (*putc)(char), const char *s) | ||||
| { | ||||
|     while (*s) putc (*s++); | ||||
| } | ||||
|  | ||||
| void puti(void (*putc)(char), int i) | ||||
| void puti(int (*putc)(char), int i) | ||||
| { | ||||
| 	unsigned int left; | ||||
| 	char buf[1 << (sizeof(int)*8) / 10]; | ||||
| @@ -64,7 +65,7 @@ void puti(void (*putc)(char), int i) | ||||
| 		putc(*p); | ||||
| } | ||||
|  | ||||
| void putx(void (*putc)(char), unsigned int i) { | ||||
| void putx(int (*putc)(char), unsigned int i) { | ||||
| 	int j; | ||||
|  | ||||
| 	puts(putc, "0x"); | ||||
| @@ -78,7 +79,7 @@ void putx(void (*putc)(char), unsigned int i) { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void putb(void (*putc)(char), unsigned int i) { | ||||
| void putb(int (*putc)(char), unsigned int i) { | ||||
| 	int j; | ||||
|  | ||||
| 	puts(putc, "0b"); | ||||
| @@ -88,7 +89,7 @@ void putb(void (*putc)(char), unsigned int i) { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| int _print(void (*putc)(char), char *fmt, va_list arg) { | ||||
| int _print(int (*putc)(char), char *fmt, va_list arg) { | ||||
| 	char *c; | ||||
| 	for (c = fmt; *c; c++) { | ||||
| 		if (*c == '%') { | ||||
| @@ -138,7 +139,7 @@ int print(char *fmt, ...) { | ||||
| } | ||||
|  | ||||
|  | ||||
| int print_func(void (*putc)(char), char *fmt, ...) { | ||||
| int print_func(int (*putc)(char), char *fmt, ...) { | ||||
| 	int ret; | ||||
| 	va_list arg; | ||||
|  | ||||
|   | ||||
| @@ -19,6 +19,7 @@ | ||||
|  */ | ||||
|  | ||||
| #include <atags.h> | ||||
| #include <init.h> | ||||
| #include <kmalloc.h> | ||||
| #include <mmu.h> | ||||
| #include <mm.h> | ||||
| @@ -26,13 +27,13 @@ | ||||
| #include <framebuffer.h> | ||||
| #include <console.h> | ||||
|  | ||||
| #include <drivers/serial.h> | ||||
|  | ||||
| #ifdef CONFIG_VEXPRESS_A9 | ||||
| #include <drivers/pl011.h> | ||||
| #include <drivers/pl111.h> | ||||
| #endif | ||||
|  | ||||
| #ifdef CONFIG_RPI | ||||
| #include <drivers/pi_mini_uart.h> | ||||
| #include <drivers/bcm2835_videocore.h> | ||||
| #endif | ||||
|  | ||||
| @@ -56,74 +57,11 @@ void video_init(void) { | ||||
| #endif | ||||
| } | ||||
|  | ||||
| void test_mm() { | ||||
| 	struct page *p, *q; | ||||
| void serial_console_init() { | ||||
| 	struct serial_dev *sdev = serial_first_device(); | ||||
|  | ||||
| 	print("\ntest_mm():\n"); | ||||
|  | ||||
| 	p = mm_get_free_pages(0); | ||||
| 	if (p) | ||||
| 		print("%x, %x\n", p, p->address); | ||||
| 	else | ||||
| 		print("Error: failed to allocate memory for p\n"); | ||||
|  | ||||
| 	q = mm_get_free_pages(4); | ||||
| 	if (q) | ||||
| 		print("%x, %x\n", q, q->address); | ||||
| 	else | ||||
| 		print("Error: failed to allocate memory for q\n"); | ||||
|  | ||||
| 	mm_put_free_pages(p); | ||||
| 	mm_put_free_pages(q); | ||||
|  | ||||
| 	q = mm_get_free_pages(1); | ||||
| 	if (q) | ||||
| 		print("%x, %x\n", q, q->address); | ||||
| 	else | ||||
| 		print("Error: failed to allocate memory for q\n"); | ||||
|  | ||||
| 	p = mm_get_free_pages(0); | ||||
| 	if (p) | ||||
| 		print("%x, %x\n", p, p->address); | ||||
| 	else | ||||
| 		print("Error: failed to allocate memory for p\n"); | ||||
| 	mm_put_free_pages(p); | ||||
| 	mm_put_free_pages(q); | ||||
|  | ||||
| } | ||||
|  | ||||
| void test_kmalloc() { | ||||
| 	void *a, *b, *c, *d; | ||||
|  | ||||
| 	print("\ntest_kmalloc():\n"); | ||||
|  | ||||
| 	a = kmalloc(4); | ||||
| 	print("a: %x\n", a); | ||||
| 	b = kmalloc(13); | ||||
| 	print("b: %x\n", b); | ||||
| 	c = kmalloc(4); | ||||
| 	print("c: %x\n", c); | ||||
| 	d = kmalloc(25); | ||||
| 	print("d: %x\n", d); | ||||
|  | ||||
| 	kfree(c); | ||||
| 	kfree(b); | ||||
| 	kfree(a); | ||||
| 	kfree(d); | ||||
|  | ||||
| 	a = kmalloc(13); | ||||
| 	print("a: %x\n", a); | ||||
| 	b = kmalloc(4); | ||||
| 	print("b: %x\n", b); | ||||
| 	c = kmalloc(25); | ||||
| 	print("c: %x\n", c); | ||||
| 	d = kmalloc(7); | ||||
| 	print("d: %x\n", d); | ||||
| } | ||||
|  | ||||
| void test_memory() { | ||||
| 	test_mm(); | ||||
| 	test_kmalloc(); | ||||
| 	if (sdev) | ||||
| 		print_init(sdev->putc); | ||||
| } | ||||
|  | ||||
| void kmalloc_init(); | ||||
| @@ -135,14 +73,9 @@ int main(void) { | ||||
| 	//setup MMU | ||||
| 	mmu_reinit(); | ||||
|  | ||||
| 	//initialize the serial console | ||||
| #ifdef CONFIG_VEXPRESS_A9 | ||||
| 	print_init(&pl011_putc); | ||||
| #endif | ||||
| #ifdef CONFIG_RPI | ||||
| 	mini_uart_init(); | ||||
| 	print_init(&mini_uart_putc); | ||||
| #endif | ||||
| 	init_earlyinitcalls(); | ||||
|  | ||||
| 	serial_console_init(); | ||||
|  | ||||
| 	//setup memory | ||||
| 	mm_init(); | ||||
| @@ -159,7 +92,7 @@ int main(void) { | ||||
| 		declare_memory_region(lower, upper); | ||||
| 	} while (!atags_next_mem_region(&atags)); | ||||
|  | ||||
| 	test_memory(); | ||||
| 	init_initcalls(); | ||||
|  | ||||
| 	video_init(); | ||||
| 	console_init(&myfb); | ||||
|   | ||||
							
								
								
									
										9
									
								
								link.ld
									
									
									
									
									
								
							
							
						
						
									
										9
									
								
								link.ld
									
									
									
									
									
								
							| @@ -4,6 +4,15 @@ SECTIONS | ||||
| { | ||||
| 	. = 0x80100000; | ||||
| 	.text : { *(.text*) *(.rodata*) } | ||||
| 	.init : { | ||||
| 		early_initcalls_start = .; | ||||
| 		*(.earlyinitcalls*) | ||||
| 		early_initcalls_end = .; | ||||
| 		initcalls_start = .; | ||||
| 		*(.driversubsysinitcalls*) | ||||
| 		*(.deviceinitcalls*) | ||||
| 		initcalls_end = .; | ||||
| 	} | ||||
| 	.data : { *(.data*) } | ||||
| 	.bss : { *(.bss*) *(COMMON*) } | ||||
| 	kernel_end = .; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user