/* * init_qtime * * Initialize the qtime entry. The timer to use has been added to the HWINFO * section as "qtimer" */ void init_qtime() { struct qtime_entry *qtime = alloc_qtime(); // alloc_qtime() call rtc_time() unsigned hwi_off = hwi_find_device("qtimer", 0); hwiattr_timer_t qtimer_attr; int r = hwiattr_get_timer(hwi_off, &qtimer_attr); long best_freq; unsigned hires_timer; unsigned i; /* both asserts tells us which operation failed */ ASSERT(hwi_off != HWI_NULL_OFF); ASSERT(r == EOK); ASSERT(qtimer_attr.common.location.len > 0); ASSERT(qtimer_attr.common.location.base != NULL); ASSERT(qtimer_attr.num_clks > 0); qtime->intr = hwitag_find_ivec(hwi_off, NULL); ASSERT(qtime->intr != HWI_ILLEGAL_VECTOR); /* if multiple clock sources have been provided, choose the highest resolution */ best_freq = 0; hires_timer = 0; // we already know there is at least 1 clock source (see ASSERT above) for (i=0; i<qtimer_attr.num_clks; i++) { long freq = hwitag_find_clkfreq(hwi_off, &i); if ((i > 0) && (freq > best_freq)) { hires_timer = i; best_freq = freq; } } ASSERT(hires_timer <= 1); // I know CLKSEL is only 1 bit (ie. only 2 clock sources) qtime->timer_scale = -15; i = hires_timer; qtime->cycles_per_sec = (uint64_t)hwitag_find_clkfreq(hwi_off, &i); // don't pass hires_timer as its changed by the call ASSERT(qtime->cycles_per_sec > 0); qtime->timer_rate = QTIME_CLK_RATE(qtime->timer_scale, qtime->cycles_per_sec); // kprintf("cps = %x %x, rate = %d\n", ((uint32_t *)&qtime->cycles_per_sec)[0], ((uint32_t *)&qtime->cycles_per_sec)[1], qtime->timer_rate); /* Map the registers (timer_base) and select the hires_timer clock source and periodic mode */ timer_base = startup_io_map(qtimer_attr.common.location.len, qtimer_attr.common.location.base); ASSERT(timer_base != NULL); out32(timer_base + EP93xx_TIMER_LOAD, UINT32_MAX); // preset the LOAD register // kprintf("clksel(%d) = 0x%x\n", hires_timer, EP93xx_TIMER_CTRL_CLKSEL(hires_timer)); out32(timer_base + EP93xx_TIMER_CTRL, EP93xx_TIMER_CTRL_CLKSEL(hires_timer) | // hires_timer holds which clock to select EP93xx_TIMER_CTRL_CLKMODE_PERIODIC); timer_start = timer_start_ep93xx; timer_diff = timer_diff_ep93xx; add_callout_array(timer_callouts, sizeof(timer_callouts)); /* start the timer */ timer_start(); }
/******************************************************************************* * ep9301_config_pic * * This routine will initialize the 2 Vectored Interrupt Controllers in the * Cirrus Logic EP93xx family of processors. * * In order to handle the number of interrupts within the EP93xx, 2 separate * interrupt controllers are cascaded together to form 1 larger controller. * There is not much documentation on how this cascading of interrupts works as * it is not a true cascade. There is some logic which allows the second * controller to be daisy chained through the first controller which is actually * connected to the processor. There is no documentation on how prioritization * works for non vectored interrupts or the prioritization of the second * controller relative to the first. The only remote suggestion that they are * handled as 32 priority interrupts starting with controller 1 followed by * controller 2 is on page 118 of the User Guide which says ... * * "Vector Control Registers. The 32 VICxVectCntl0 through VICxVectCnt15 * registers select the interrupt source for the vectored interrupt." * * My interpretation is that vector 0 through 15 on controller 1 followed by * vector 0 through 15 on controller 2 is the prioritization. * * This still leaves the problem that we don't know which controller to look * at first without looking at the status register for each ... which implies * that the prioritization is completely under software control. In other words, * software must decide to either read controller 1 or 2 status first, effectively * prioritizing one set of 32 interrupts over the other 32. Then, because some of * the interrupts are not vectored, we must choose bits in the status register. * However, if the vector address read when none of the interrupts programmed as * vectored interrupts is the default vector, then the sequence can flow as * follows (I will prioritize controller 1 interrupts over controller 2) * * if (vic1_status != 0) * { * if (vic1_vector != default vector) * id = vect_table[vic1_vector] * else * id = first bit set in vic1_status * } * else if (vic2_status != 0) * { * if (vic1_vector != default vector) * id = vect_table[vic2_vector] * else * id = first bit set in vic2_status * } * else * spurious interrupt * * return id * * This is all greatly complicated by the fact that only 1/2 of the interrupts * in each controller are vectored and prioritized not to mention the * prioritization between IRQ and FIQ interrupts. * To properly support prioritized interrupts and decouple them from the assigned * internal id's is going to require some more thought so for now I am not going * to use the hardware prioritization of the vectored interrupts and instead * simply do a first bit set prioritization of interrupts as follows * * VIC1, 31 -> 0 * VIC2, 31 -> 0 * * implying that in the case of multiple occurring interrupts, VIC1, interrupt * 31 (documented interrupt source 31) is the highest in the system and VIC2, * interrupt 0 (documented interrupt source 32) is the lowest. * * Additionally, FIQ is not going to be used for now. * * Returns: the number of interrupt vectors assigned * * Implementation Note: * */ uint32_t ep9301_config_pic(uint32_t os_vector_base, uint32_t pic_vector_base, vector_tbl_t *reg_vectors) { uintptr_t base = startup_io_map((EP93xx_VIC_CTRL2_BASE - EP93xx_VIC_CTRL1_BASE) + EP93xx_VIC_CTRL2_SIZE, EP93xx_VIC_CTRL1_BASE); unsigned hwi_off; unsigned i; /* * check the peripheral ID registers. We will assert on the truth of valid_pic_id() since a change * may suggest a difference in the device behaviour which may need to be reflected in a * code change */ ASSERT(valid_pic_id()); ASSERT(pic_vector_base == os_vector_base); // re-mapping not supported (yet) reg_vectors->start = pic_vector_base; reg_vectors->num = 0; /* set all the vector bases */ intrs[0].vector_base = os_vector_base; ASSERT(intrs[0].num_vectors > 0); reg_vectors->num += intrs[0].num_vectors; for (i=1; i<NUM_ELTS(intrs); i++) { intrs[i].vector_base = intrs[i - 1].vector_base + intrs[i - 1].num_vectors; ASSERT(intrs[i].num_vectors > 0); reg_vectors->num += intrs[i].num_vectors; } /* add the interrupt info section */ add_interrupt_array(intrs, sizeof(intrs)); /* disable and clear all interrupts. Note that we do not check EP93xx_VIC_INT_RAW because * some devices do actually have interrupts asserted and this is reflected in the RAW register(s) */ out32(VIC1(EP93xx_VIC_INT_CLEAR), 0xFFFFFFFFU); out32(VIC1(EP93xx_VIC_SWINT_CLEAR), 0xFFFFFFFFU); ASSERT(in32(VIC1(EP93xx_VIC_INT_ENABLE)) == 0); ASSERT(in32(VIC1(EP93xx_VIC_SWINT_ENABLE)) == 0); ASSERT(in32(VIC1(EP93xx_VIC_IRQ_STATUS)) == 0); ASSERT(in32(VIC1(EP93xx_VIC_FIQ_STATUS)) == 0); out32(VIC2(EP93xx_VIC_INT_CLEAR), 0xFFFFFFFFU); out32(VIC2(EP93xx_VIC_SWINT_CLEAR), 0xFFFFFFFFU); ASSERT(in32(VIC2(EP93xx_VIC_INT_ENABLE)) == 0); ASSERT(in32(VIC2(EP93xx_VIC_SWINT_ENABLE)) == 0); ASSERT(in32(VIC2(EP93xx_VIC_IRQ_STATUS)) == 0); ASSERT(in32(VIC2(EP93xx_VIC_FIQ_STATUS)) == 0); /* set privileged access to interrupt controller registers */ out32(VIC1(EP93xx_VIC_PROTECTION), 0x0); out32(VIC2(EP93xx_VIC_PROTECTION), 0x0); /* by default, all interrupts will generate an IRQ (FIQ not used) */ out32(VIC1(EP93xx_VIC_INT_SELECT), 0); out32(VIC2(EP93xx_VIC_INT_SELECT), 0); /* place HWI_ILLEGAL_VECTOR into the default vector(s) */ out32(VIC1(EP93xx_VIC_VEC_ADDR_DFLT), HWI_ILLEGAL_VECTOR); out32(VIC2(EP93xx_VIC_VEC_ADDR_DFLT), HWI_ILLEGAL_VECTOR); /* disable the use of vectored interrupts for now */ in32(VIC1(EP93xx_VIC_VEC_ADDR_CURR)); out32(VIC1(EP93xx_VIC_VEC_ADDR_CURR), 0); in32(VIC2(EP93xx_VIC_VEC_ADDR_CURR)); out32(VIC2(EP93xx_VIC_VEC_ADDR_CURR), 0); for (i=0; i<16; i++) { out32(VIC1(EP93xx_VIC_VEC_ADDR(i)), HWI_ILLEGAL_VECTOR); out32(VIC1(EP93xx_VIC_VEC_CTRL(i)), 0); out32(VIC2(EP93xx_VIC_VEC_ADDR(i)), HWI_ILLEGAL_VECTOR); out32(VIC2(EP93xx_VIC_VEC_CTRL(i)), 0); } /* TIMER1 - interrupt 4 */ hwitag_set_avail_ivec(hwi_find_device(EP93xx_HWI_TIMER, 0), -1, 4); /* TIMER2 - interrupt 5 */ hwitag_set_avail_ivec(hwi_find_device(EP93xx_HWI_TIMER, 1), -1, 5); /* TIMER3 - interrupt 51 */ hwitag_set_avail_ivec(hwi_find_device(EP93xx_HWI_TIMER, 2), -1, 51); /* WATCHDOG TIMER - interrupt 36 */ hwitag_set_avail_ivec(hwi_find_device(EP93xx_HWI_WDOG, 0), -1, 36); /* DMA channels - Note that the order of vector assignment in the HWINFO section will be * memory to memory 0 - 17 * memory to memory 1 - 18 * memory to peripheral 0 - 7 * memory to peripheral 1 - 8 * memory to peripheral 2 - 9 * memory to peripheral 3 - 10 * memory to peripheral 4 - 11 * memory to peripheral 5 - 12 * memory to peripheral 6 - 13 * memory to peripheral 7 - 14 * memory to peripheral 8 - 15 * memory to peripheral 9 - 16 */ if ((hwi_off = hwi_find_device(EP93xx_HWI_DMA, 0)) != HWI_NULL_OFF) { hwitag_set_ivec(hwi_off, 0, 17); hwitag_set_ivec(hwi_off, 1, 18); hwitag_set_ivec(hwi_off, 2, 7); hwitag_set_ivec(hwi_off, 3, 8); hwitag_set_ivec(hwi_off, 4, 9); hwitag_set_ivec(hwi_off, 5, 10); hwitag_set_ivec(hwi_off, 6, 11); hwitag_set_ivec(hwi_off, 7, 12); hwitag_set_ivec(hwi_off, 8, 13); hwitag_set_ivec(hwi_off, 9, 14); hwitag_set_ivec(hwi_off, 10, 15); hwitag_set_ivec(hwi_off, 11, 16); } /* GPIO * 19 of the EP9301 GPIO ports are interruptible and are handled via a * cascaded interrupt entry. In order for devices specifically connected to * those GPIO pins to have a driver InterruptAttach() we provide the assigned * vector ranges with 3 irqrange tags corresponding to the PORTA, PORTB and * PORTF interrupting port pins respectively. Within the range tag assignments, * the 'irq' field corresponds to the LSb (pin 0 of the port) and 'irq' + 'num' - 1 * corresponds to the MSb (pin 7 of the port). */ if ((hwi_off = hwi_find_device(EP93xx_HWI_GPIO, 0)) != HWI_NULL_OFF) { const unsigned num_pAB_vectors = intrs[1].num_vectors / 2; const unsigned num_pF_vectors = intrs[2].num_vectors + intrs[3].num_vectors + intrs[4].num_vectors; hwitag_set_ivecrange(hwi_off, 0, intrs[1].vector_base, num_pAB_vectors); // PORT A hwitag_set_ivecrange(hwi_off, 1, intrs[1].vector_base + num_pAB_vectors, num_pAB_vectors); // PORT B hwitag_set_ivecrange(hwi_off, 2, intrs[2].vector_base, num_pF_vectors); // PORT F } /* UART1 - Note that the order of vector assignment in the HWINFO section will be * combined - 52 * Rx - 23 * Tx - 24 */ if ((hwi_off = hwi_find_device(EP93xx_HWI_UART, 0)) != HWI_NULL_OFF) { hwitag_set_ivec(hwi_off, 0, 52); hwitag_set_ivec(hwi_off, 1, 23); hwitag_set_ivec(hwi_off, 2, 24); } /* UART2 - Note that the order of vector assignment in the HWINFO section will be * combined - 54 * Rx - 25 * Tx - 26 */ if ((hwi_off = hwi_find_device(EP93xx_HWI_UART, 1)) != HWI_NULL_OFF) { hwitag_set_ivec(hwi_off, 0, 54); hwitag_set_ivec(hwi_off, 1, 25); hwitag_set_ivec(hwi_off, 2, 26); } /* EXTERNAL IRQ - 2 interrupts 32, 33 and 40 for IQR0, IRQ1 and IRQ2 respectively */ if ((hwi_off = hwi_find_device(EP93xx_HWI_EXT_IRQ, 0)) != HWI_NULL_OFF) { hwitag_set_ivec(hwi_off, 0, 32); hwitag_set_ivec(hwi_off, 1, 33); hwitag_set_ivec(hwi_off, 2, 40); } /* RTC - Note that the order of vector assignment in the HWINFO section will be * rtc - 37 * 64 Hz clock - 35 * 1 Hz clock - 42 (edge sensitive) */ if ((hwi_off = hwi_find_device(EP93xx_HWI_RTC, 0)) != HWI_NULL_OFF) { hwitag_set_ivec(hwi_off, 0, 37); hwitag_set_ivec(hwi_off, 1, 35); hwitag_set_ivec(hwi_off, 2, 42); } /* IrDA Device - 38 */ if ((hwi_off = hwi_find_device(EP93xx_HWI_IrDA, 0)) != HWI_NULL_OFF) { hwitag_set_avail_ivec(hwi_off, 0, 38); } /* Ethernet Device */ if ((hwi_off = hwi_find_device(EP93xx_HWI_ENET, 0)) != HWI_NULL_OFF) { hwitag_set_avail_ivec(hwi_off, 0, 39); } /* SSP - Note that the order of vector assignment in the HWINFO section will be * combined - 53 * Rx - 45 * Tx - 46 */ if ((hwi_off = hwi_find_bus(EP93xx_HWI_SSP, 0)) != HWI_NULL_OFF) { hwitag_set_ivec(hwi_off, 0, 53); hwitag_set_ivec(hwi_off, 1, 45); hwitag_set_ivec(hwi_off, 2, 46); } /* USB host controller - 56 */ if ((hwi_off = hwi_find_bus(EP93xx_HWI_USB, 0)) != HWI_NULL_OFF) { hwitag_set_avail_ivec(hwi_off, 0, 56); } /* I2S device - 60 */ if ((hwi_off = hwi_find_device(EP93xx_HWI_I2S, 0)) != HWI_NULL_OFF) { hwitag_set_avail_ivec(hwi_off, 0, 60); } /* ACC (AC97) device - 6 */ if ((hwi_off = hwi_find_device(EP93xx_HWI_AUDIO, 0)) != HWI_NULL_OFF) { hwitag_set_avail_ivec(hwi_off, 0, 6); } startup_io_unmap(base); return reg_vectors->num; }
void hwi_imx51() { unsigned hwi_bus_internal = 0; /* add I2C (unless directed not to) */ { unsigned hwi_off; hwiattr_i2c_t attr = HWIATTR_I2C_T_INITIALIZER; HWIATTR_I2C_SET_NUM_IRQ(&attr, 1); /* create i2c0 and set the clock source */ HWIATTR_I2C_SET_LOCATION(&attr, MX51_I2C1_BASE, MX51_I2C_SIZE, 0, hwi_find_as(MX51_I2C1_BASE, 1)); hwi_off = hwibus_add_i2c(hwi_bus_internal, &attr); ASSERT(hwi_find_unit(hwi_off) == 0); hwitag_set_ivec(hwi_off, 0, MX51_I2C1_IRQ); /* create i2c1 and set the clock source */ HWIATTR_I2C_SET_LOCATION(&attr, MX51_I2C2_BASE, MX51_I2C_SIZE, 0, hwi_find_as(MX51_I2C2_BASE, 1)); hwi_off = hwibus_add_i2c(hwi_bus_internal, &attr); ASSERT(hwi_find_unit(hwi_off) == 1); hwitag_set_ivec(hwi_off, 0, MX51_I2C2_IRQ); } /* add UART */ { unsigned hwi_off; hwiattr_uart_t attr = HWIATTR_UART_T_INITIALIZER; struct hwi_inputclk clksrc = {.clk = uart_clock, .div = 16}; HWIATTR_UART_SET_NUM_IRQ(&attr, 1); HWIATTR_UART_SET_NUM_CLK(&attr, 1); /* create uart0 */ HWIATTR_UART_SET_LOCATION(&attr, MX51_UART1_BASE, MX51_UART_SIZE, 0, hwi_find_as(MX51_UART1_BASE, 1)); hwi_off = hwidev_add_uart(IMX51_HWI_UART, &attr, hwi_bus_internal); ASSERT(hwi_find_unit(hwi_off) == 0); hwitag_set_ivec(hwi_off, 0, MX51_UART1_INTR); hwitag_set_inputclk(hwi_off, 0, &clksrc); /* historically the UART's were called 'mxl' so add these synonyms */ hwi_add_synonym(hwi_find_device(IMX51_HWI_UART, 0), "sermxl"); } /*add the FEC */ { unsigned hwi_off; hwiattr_enet_t attr = HWIATTR_ENET_T_INITIALIZER; HWIATTR_USB_SET_NUM_IRQ(&attr, 1); /* create eTSEC0 and set the PHY address */ HWIATTR_ENET_SET_LOCATION(&attr, MX51_FEC_BASE, 0x4000, 0, hwi_find_as(MX51_FEC_BASE, 1)); hwi_off = hwidev_add_enet("fec", &attr, hwi_bus_internal); ASSERT(hwi_find_unit(hwi_off) == 0); hwitag_set_avail_ivec(hwi_off, 0, MX51_FEC_INTR); } /* add 1 USB controller */ { unsigned hwi_off; hwiattr_usb_t attr = HWIATTR_USB_T_INITIALIZER; HWIATTR_USB_SET_NUM_IRQ(&attr, 1); /* create usb0 */ HWIATTR_USB_SET_LOCATION(&attr, MX51_USBH1_BASE, MX51_USB_SIZE, 0, hwi_find_as(MX51_USBH1_BASE, 1)); hwi_off = hwibus_add_usb(hwi_bus_internal, &attr); ASSERT(hwi_off != HWI_NULL_OFF); hwitag_set_avail_ivec(hwi_off, 0, MX51_USB_INTR); } /* add 1 sdma controllers */ { unsigned hwi_off; hwiattr_dma_t attr = HWIATTR_DMA_T_INITIALIZER; HWIATTR_DMA_SET_NUM_IRQ(&attr, 1); /* create DMA controller 0 */ HWIATTR_USB_SET_LOCATION(&attr, MX51_SDMA_BASE, MX51_SDMA_SIZE, 0, hwi_find_as(MX51_SDMA_BASE, 1)); hwi_off = hwidev_add_dma(IMX51_HWI_DMA, &attr, hwi_bus_internal); ASSERT(hwi_find_unit(hwi_off) == 0); hwitag_set_avail_ivec(hwi_off, 0, MX51_SDMA_INTRU); } /* add the SSI device */ { unsigned hwi_off = hwidev_add("ssi", hwi_devclass_NONE, HWI_NULL_OFF); hwiattr_common_t attr = HWIATTR_COMMON_INITIALIZER; ASSERT(hwi_off != HWI_NULL_OFF); HWIATTR_SET_LOCATION(&attr, MX51_SSI1_BASE, MX51_SSI_SIZE, 0, hwi_find_as(MX51_SSI1_BASE, 1)); hwitag_add_common(hwi_off, &attr); } /*add the SDRAM */ { unsigned hwi_off = hwidev_add("sdram", hwi_devclass_NONE, HWI_NULL_OFF); hwiattr_common_t attr = HWIATTR_COMMON_INITIALIZER; ASSERT(hwi_off != HWI_NULL_OFF); HWIATTR_SET_LOCATION(&attr, MX51_SDRAM_BASE, MX51_SDRAM_SIZE, 0, hwi_find_as(MX51_SDRAM_BASE, 1)); hwitag_add_common(hwi_off, &attr); } /* add the WATCHDOG device */ { unsigned hwi_off; hwiattr_timer_t attr = HWIATTR_TIMER_T_INITIALIZER; const struct hwi_inputclk clksrc_kick = {.clk = 10, .div = 1}; HWIATTR_TIMER_SET_NUM_CLK(&attr, 1); HWIATTR_TIMER_SET_LOCATION(&attr, MX51_WDOG1_BASE, MX51_WDOG_SIZE, 0, hwi_find_as(MX51_WDOG1_BASE, 1)); hwi_off = hwidev_add_timer("wdog", &attr, HWI_NULL_OFF); ASSERT(hwi_off != HWI_NULL_OFF); hwitag_set_inputclk(hwi_off, 0, (struct hwi_inputclk *)&clksrc_kick); } } #if defined(__QNXNTO__) && defined(__USESRCVERSION) #include <sys/srcversion.h> __SRCVERSION("$URL: http://svn/product/branches/6.6.0/trunk/hardware/startup/lib/arm/hwi_imx51.c $ $Rev: 680332 $")