Hardware initialization

The startup code does the minimum needed to detect the hardware configuration and completes any other configuration that might be required for the OS kernel to run.

The source code for initialization routines can be found in BSPs in the src/hardware/startup/boards/boardname/ and src/hardware/startup/lib/ directories. For example:

src/hardware/startup/boards/dra74x/init_qtime.c

The startup/boards/boardname directory has the platform- and platform variant-specific source code. If the source code is generic for the architecture (which is more common with x86 platforms), the source code is in the startup/lib/ directory.

A typical startup main() function calls the following hardware initialization functions in this order (see Source code structure in this chapter):

  1. init_raminfo()
  2. init_mmu()
  3. init_intrinfo()
  4. init_qtime()
  5. init_cacheattr()
  6. init_cpuinfo()

Many BSPs also include additional hardware initialization functions for supported technologies; you will find these in appropriately named files (e.g., init_sata.c, init_usb.c). For more information, see the init_*() functions in the Startup Library.

Adding hardware initialization code

If you need to add some code to look after hardware initialization that isn't covered by one of the int_*() functions in the library (or your custom version of one of these functions), you can do one of the following:

Below is an example of a small amount of code added to the main.c file for a fictitious Fortinbras board:

// Announce start of Startup code   
for (i = 0; i < 8; ++i)   
	led[i] = startup_msg[i]; 

Setting interrupt sensitivity

Most systems today use only level-sensitive interrupts. QNX Neutrino supports both level-sensitive interrupts and edge-sensitive interrupts to support legacy hardware (e.g., Intel x86 hardware with the 8259 PIC on an ISA bus).

It is the responsibility of the interrupt controller initialization code in conjunction with the interrupt kernel callouts to specify the interrupt sensitivity (see Interrupt controller for information about interrupt kernel callouts).

The code snippet below is from the gic_v2_init() function, which performs some GIC initializations during startup of a 64-bit ARM board:

/*
 * Default all SPI interrupts as level triggered
 */
for (i = 32; i < itn; i += 16) {
	out32(gicd + ARM_GICD_ICFGRn + (i * 4 / 16), 0);
}

On this board, the sensitivity information for each interrupt ID is stored in the GIC's ICFGR registers. Zero (0) means level-sensitive interrupts. The code snippet above writes 0 to these registers. Check your board's documentation to learn what registers you need to set (if any) and to what values to enable level-sensitive interrupt support.

Below, for context, is the complete GIC interrupt initialization function from which the snippet is taken:

gic_v2_init(paddr_t gicd, paddr_t gicc)
{
	struct aarch64_gic_map_entry	*gic_map = lsp.cpu.aarch64_gic_map.p;
	unsigned	gic_cpu;
	unsigned	itn;
	unsigned	i;

	gic_gicd = gicd;
	gic_gicc = gicc;
	gic_cpu_init = gic_v2_gicc_init;
	gic_sendipi = &sendipi_gic_v2;

	/*
	 * Initialise the GIC cpu map with invalid values.
	 * gic_v2_gicc_init() will set the mapping for each cpu as it comes up.
	 */
	for (i = 0; i < lsp.syspage.p->num_cpu; i++) {
		gic_map->gic_cpu[i] = ~0u;
	}
	gic_cpu = gic_v2_cpunum();

	/*
	 * Disable distributor
	 */
	out32(gicd + ARM_GICD_CTLR, 0);

	/*
	 * Calculate number of interrupt lines.
	 */
	itn = ((in32(gicd + ARM_GICD_TYPER) & ARM_GICD_TYPER_ITLN) + 1) * 32;
	gic_v2_intr.num_vectors = itn;

	if (debug_flag) {
		kprintf("GICv2: %d interrupts\n", itn);
	}

	/*
	 * Disable all interrupts and clear pending state
	 */
	for (i = 0; i < itn; i += 32) {
		out32(gicd + ARM_GICD_ICENABLERn + (i * 4 / 32), 0xffffffff);
		out32(gicd + ARM_GICD_ICPENDRn + (i * 4 / 32), 0xffffffff);
	}

	/*
	 * Set default priority of all interrupts to 0xa0
	 */
	for (i = 0; i < itn; i += 4) {
		out32(gicd + ARM_GICD_IPRIORITYn + i, 0xa0a0a0a0);
	}

	/*
	 * Route all SPI interrupts to cpu0
	 */
	if (debug_flag) {
		kprintf("GICv2: routing SPIs to gic cpu %d\n", gic_cpu);
	}
	for (i = 32; i < itn; i += 4) {
		out32(gicd + ARM_GICD_ITARGETSRn + i, (0x01010101u << gic_cpu));
	}

	/*
	 * Default all SPI interrupts as level triggered
	 */
	for (i = 32; i < itn; i += 16) {
		out32(gicd + ARM_GICD_ICFGRn + (i * 4 / 16), 0);
	}

	/*
	 * Enable distributor - cpu interface is initialised via init_cpuinfo().
	 */
	out32(gicd + ARM_GICD_CTLR, ARM_GICD_CTLR_EN);

	/*
	 * Add the interrupt callouts
	 */
	add_interrupt(&gic_v2_intr);
}
Note:

A programmer's use of interrupt-related functions such as InterruptAttach() or InterruptMask() isn't affected by the interrupt sensitivity, though you should be aware of the implications of the interrupt sensitivity on your system, as this may affect what you must do when working with interrupts.

For more information about interrupts, see the Interrupts chapter in Getting Started with QNX Neutrino, and the Writing an Interrupt Handler chapter in the Programmer's Guide.