static void stm32_exti_writew(void *opaque, hwaddr offset, uint64_t value) { Stm32Exti *s = (Stm32Exti *)opaque; int pos, bit_value; if(offset <= EXTI_EMR_OFFSET) { switch (offset) { case EXTI_IMR_OFFSET: s->EXTI_IMR = value; break; case EXTI_EMR_OFFSET: /* Do nothing, events are not implemented yet. * But we don't want to throw an error. */ break; default: STM32_BAD_REG(offset, WORD_ACCESS_SIZE); break; } } else { /* These registers all contain one bit per EXTI line. We will loop * through each line and then update each bit in the appropriate * register. */ for(pos = 0; pos < EXTI_LINE_COUNT; pos++) { bit_value = GET_BIT_VALUE(value, pos); switch (offset) { case EXTI_RTSR_OFFSET: update_TSR_bit(s, &(s->EXTI_RTSR), pos, bit_value); break; case EXTI_FTSR_OFFSET: update_TSR_bit(s, &(s->EXTI_FTSR), pos, bit_value); break; case EXTI_SWIER_OFFSET: /* If the Software Interrupt Event Register is changed * from 0 to 1, trigger an interrupt. Changing the * bit to 0 does nothing. */ if(bit_value == 1) { if(GET_BIT_VALUE(s->EXTI_SWIER, pos) == 0) { SET_BIT(s->EXTI_SWIER, pos); stm32_exti_trigger(s, pos); } } break; case EXTI_PR_OFFSET: /* When a 1 is written to a PR bit, it actually clears the * PR bit. */ if(bit_value == 1) { stm32_exti_change_EXTI_PR_bit(s, pos, 0); } break; default: STM32_BAD_REG(offset, WORD_ACCESS_SIZE); break; } } } }
/* We will assume that this handler will only be called if the pin actually * changed state. */ static void stm32_exti_gpio_in_handler(void *opaque, int n, int level) { Stm32Exti *s = (Stm32Exti *)opaque; unsigned pin = n; assert(pin < EXTI_LINE_COUNT); /* Check the level - if it is rising, then trigger an interrupt if the * corresponding Rising Trigger Selection Register flag is set. Otherwise, * trigger if the Falling Trigger Selection Register flag is set. */ if((level && GET_BIT_VALUE(s->EXTI_RTSR, pin)) || (!level && GET_BIT_VALUE(s->EXTI_FTSR, pin))) { stm32_exti_trigger(s, pin); } }
/* Update the Pending Register. This will trigger an interrupt if a bit is * set. */ static void stm32_exti_change_EXTI_PR_bit(Stm32Exti *s, unsigned pos, unsigned new_bit_value) { unsigned old_bit_value; assert((new_bit_value == 0) || (new_bit_value == 1)); assert(pos < EXTI_LINE_COUNT); old_bit_value = GET_BIT_VALUE(s->EXTI_PR, pos); /* Only continue if the PR bit is actually changing value. */ if(new_bit_value != old_bit_value) { /* If the bit is being reset, the corresponding Software Interrupt Event * Register bit is automatically reset. */ if(!new_bit_value) { RESET_BIT(s->EXTI_SWIER, pos); } /* Update the IRQ for this EXTI line. Some lines share the same * NVIC IRQ. */ if(pos <= 4) { /* EXTI0 - EXTI4 each have their own NVIC IRQ */ qemu_set_irq(s->irq[pos], new_bit_value); } else if(pos <= 9) { /* EXTI5 - EXTI9 share an NVIC IRQ */ qemu_set_irq(s->irq[5], new_bit_value); } else if(pos <= 15) { /* EXTI10 - EXTI15 share an NVIC IRQ */ qemu_set_irq(s->irq[6], new_bit_value); } else if(pos == 16) { /* PVD IRQ */ qemu_set_irq(s->irq[7], new_bit_value); } else if(pos == 17) { /* RTCAlarm IRQ */ qemu_set_irq(s->irq[8], new_bit_value); } else if(pos == 18) { /* OTG_FS_WKUP IRQ */ qemu_set_irq(s->irq[9], new_bit_value); } else if(pos == 19) { /* ETH_WKUP IRQ */ qemu_set_irq(s->irq[10], new_bit_value); } else if(pos == 20) { /* OTG_HS_WKUP IRQ */ qemu_set_irq(s->irq[11], new_bit_value); } else if(pos == 21) { /* TAMP_STAMP IRQ */ qemu_set_irq(s->irq[12], new_bit_value); } else if(pos == 22) { /* RTC_WKUP IRQ */ qemu_set_irq(s->irq[13], new_bit_value); } else { assert(false); } /* Update the register. */ CHANGE_BIT(s->EXTI_PR, pos, new_bit_value); } }
/* Update a Trigger Selection Register (both the Rising and Falling TSR * registers are handled by this routine). */ static void update_TSR_bit(Stm32Exti *s, uint32_t *tsr_register, unsigned pos, unsigned new_bit_value) { assert((new_bit_value == 0) || (new_bit_value == 1)); assert(pos < EXTI_LINE_COUNT); if(new_bit_value != GET_BIT_VALUE(*tsr_register, pos)) { /* According to the documentation, the Pending register is cleared when * the "sensitivity of the edge detector changes. Is this right??? */ stm32_exti_change_EXTI_PR_bit(s, pos, 0); } CHANGE_BIT(*tsr_register, pos, new_bit_value); }
static void stm32_rcc_RCC_CFGR_write(Stm32Rcc *s, uint32_t new_value, bool init) { uint32_t new_PLLMUL, new_PLLXTPRE, new_PLLSRC; /* PLLMUL */ new_PLLMUL = (new_value & RCC_CFGR_PLLMUL_MASK) >> RCC_CFGR_PLLMUL_START; if(!init) { if(clktree_is_enabled(s->PLLCLK) && (new_PLLMUL != s->RCC_CFGR_PLLMUL)) { stm32_hw_warn("Can only change PLLMUL while PLL is disabled"); } } assert(new_PLLMUL <= 0xf); if(new_PLLMUL == 0xf) { clktree_set_scale(s->PLLCLK, 16, 1); } else { clktree_set_scale(s->PLLCLK, new_PLLMUL + 2, 1); } s->RCC_CFGR_PLLMUL = new_PLLMUL; /* PLLXTPRE */ new_PLLXTPRE = GET_BIT_VALUE(new_value, RCC_CFGR_PLLXTPRE_BIT); if(!init) { if(clktree_is_enabled(s->PLLCLK) && (new_PLLXTPRE != s->RCC_CFGR_PLLXTPRE)) { stm32_hw_warn("Can only change PLLXTPRE while PLL is disabled"); } } clktree_set_selected_input(s->PLLXTPRECLK, new_PLLXTPRE); s->RCC_CFGR_PLLXTPRE = new_PLLXTPRE; /* PLLSRC */ new_PLLSRC = GET_BIT_VALUE(new_value, RCC_CFGR_PLLSRC_BIT); if(!init) { if(clktree_is_enabled(s->PLLCLK) && (new_PLLSRC != s->RCC_CFGR_PLLSRC)) { stm32_hw_warn("Can only change PLLSRC while PLL is disabled"); } } clktree_set_selected_input(s->PLLCLK, new_PLLSRC); s->RCC_CFGR_PLLSRC = new_PLLSRC; /* PPRE2 */ s->RCC_CFGR_PPRE2 = (new_value & RCC_CFGR_PPRE2_MASK) >> RCC_CFGR_PPRE2_START; if(s->RCC_CFGR_PPRE2 < 0x4) { clktree_set_scale(s->PCLK2, 1, 1); } else { clktree_set_scale(s->PCLK2, 1, 2 * (s->RCC_CFGR_PPRE2 - 3)); } /* PPRE1 */ s->RCC_CFGR_PPRE1 = (new_value & RCC_CFGR_PPRE1_MASK) >> RCC_CFGR_PPRE1_START; if(s->RCC_CFGR_PPRE1 < 4) { clktree_set_scale(s->PCLK1, 1, 1); } else { clktree_set_scale(s->PCLK1, 1, 2 * (s->RCC_CFGR_PPRE1 - 3)); } /* HPRE */ s->RCC_CFGR_HPRE = (new_value & RCC_CFGR_HPRE_MASK) >> RCC_CFGR_HPRE_START; if(s->RCC_CFGR_HPRE < 8) { clktree_set_scale(s->HCLK, 1, 1); } else { clktree_set_scale(s->HCLK, 1, 2 * (s->RCC_CFGR_HPRE - 7)); } /* SW */ s->RCC_CFGR_SW = (new_value & RCC_CFGR_SW_MASK) >> RCC_CFGR_SW_START; switch(s->RCC_CFGR_SW) { case 0x0: case 0x1: case 0x2: clktree_set_selected_input(s->SYSCLK, s->RCC_CFGR_SW); break; default: hw_error("Invalid input selected for SYSCLK"); break; } }