예제 #1
0
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;
            }
        }
    }
}
예제 #2
0
/* 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);
    }
}
예제 #3
0
/* 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);
    }
}
예제 #4
0
/* 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);
}
예제 #5
0
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;
    }
}