1a50c0d6fSJean-Christophe DUBOIS /* 2a50c0d6fSJean-Christophe DUBOIS * IMX GPT Timer 3a50c0d6fSJean-Christophe DUBOIS * 4a50c0d6fSJean-Christophe DUBOIS * Copyright (c) 2008 OK Labs 5a50c0d6fSJean-Christophe DUBOIS * Copyright (c) 2011 NICTA Pty Ltd 6a50c0d6fSJean-Christophe DUBOIS * Originally written by Hans Jiang 7a50c0d6fSJean-Christophe DUBOIS * Updated by Peter Chubb 8d647b26dSJean-Christophe Dubois * Updated by Jean-Christophe Dubois <jcd@tribudubois.net> 9a50c0d6fSJean-Christophe DUBOIS * 10a50c0d6fSJean-Christophe DUBOIS * This code is licensed under GPL version 2 or later. See 11a50c0d6fSJean-Christophe DUBOIS * the COPYING file in the top-level directory. 12a50c0d6fSJean-Christophe DUBOIS * 13a50c0d6fSJean-Christophe DUBOIS */ 14a50c0d6fSJean-Christophe DUBOIS 158ef94f0bSPeter Maydell #include "qemu/osdep.h" 1664552b6bSMarkus Armbruster #include "hw/irq.h" 17d647b26dSJean-Christophe Dubois #include "hw/timer/imx_gpt.h" 18d6454270SMarkus Armbruster #include "migration/vmstate.h" 190b8fa32fSMarkus Armbruster #include "qemu/module.h" 2003dd024fSPaolo Bonzini #include "qemu/log.h" 21a50c0d6fSJean-Christophe DUBOIS 2205453526SJean-Christophe Dubois #ifndef DEBUG_IMX_GPT 2305453526SJean-Christophe Dubois #define DEBUG_IMX_GPT 0 2405453526SJean-Christophe Dubois #endif 2505453526SJean-Christophe Dubois 2605453526SJean-Christophe Dubois #define DPRINTF(fmt, args...) \ 2705453526SJean-Christophe Dubois do { \ 2805453526SJean-Christophe Dubois if (DEBUG_IMX_GPT) { \ 2905453526SJean-Christophe Dubois fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX_GPT, \ 3005453526SJean-Christophe Dubois __func__, ##args); \ 3105453526SJean-Christophe Dubois } \ 3205453526SJean-Christophe Dubois } while (0) 335ec694b5SJean-Christophe DUBOIS 34d675765aSPeter Maydell static const char *imx_gpt_reg_name(uint32_t reg) 355ec694b5SJean-Christophe DUBOIS { 365ec694b5SJean-Christophe DUBOIS switch (reg) { 375ec694b5SJean-Christophe DUBOIS case 0: 385ec694b5SJean-Christophe DUBOIS return "CR"; 395ec694b5SJean-Christophe DUBOIS case 1: 405ec694b5SJean-Christophe DUBOIS return "PR"; 415ec694b5SJean-Christophe DUBOIS case 2: 425ec694b5SJean-Christophe DUBOIS return "SR"; 435ec694b5SJean-Christophe DUBOIS case 3: 445ec694b5SJean-Christophe DUBOIS return "IR"; 455ec694b5SJean-Christophe DUBOIS case 4: 465ec694b5SJean-Christophe DUBOIS return "OCR1"; 475ec694b5SJean-Christophe DUBOIS case 5: 485ec694b5SJean-Christophe DUBOIS return "OCR2"; 495ec694b5SJean-Christophe DUBOIS case 6: 505ec694b5SJean-Christophe DUBOIS return "OCR3"; 515ec694b5SJean-Christophe DUBOIS case 7: 525ec694b5SJean-Christophe DUBOIS return "ICR1"; 535ec694b5SJean-Christophe DUBOIS case 8: 545ec694b5SJean-Christophe DUBOIS return "ICR2"; 555ec694b5SJean-Christophe DUBOIS case 9: 565ec694b5SJean-Christophe DUBOIS return "CNT"; 575ec694b5SJean-Christophe DUBOIS default: 585ec694b5SJean-Christophe DUBOIS return "[?]"; 595ec694b5SJean-Christophe DUBOIS } 605ec694b5SJean-Christophe DUBOIS } 615ec694b5SJean-Christophe DUBOIS 6267110c3eSJean-Christophe DUBOIS static const VMStateDescription vmstate_imx_timer_gpt = { 6368b85290SJean-Christophe Dubois .name = TYPE_IMX_GPT, 645ec694b5SJean-Christophe DUBOIS .version_id = 3, 655ec694b5SJean-Christophe DUBOIS .minimum_version_id = 3, 66a50c0d6fSJean-Christophe DUBOIS .fields = (VMStateField[]) { 6767110c3eSJean-Christophe DUBOIS VMSTATE_UINT32(cr, IMXGPTState), 6867110c3eSJean-Christophe DUBOIS VMSTATE_UINT32(pr, IMXGPTState), 6967110c3eSJean-Christophe DUBOIS VMSTATE_UINT32(sr, IMXGPTState), 7067110c3eSJean-Christophe DUBOIS VMSTATE_UINT32(ir, IMXGPTState), 7167110c3eSJean-Christophe DUBOIS VMSTATE_UINT32(ocr1, IMXGPTState), 7267110c3eSJean-Christophe DUBOIS VMSTATE_UINT32(ocr2, IMXGPTState), 7367110c3eSJean-Christophe DUBOIS VMSTATE_UINT32(ocr3, IMXGPTState), 7467110c3eSJean-Christophe DUBOIS VMSTATE_UINT32(icr1, IMXGPTState), 7567110c3eSJean-Christophe DUBOIS VMSTATE_UINT32(icr2, IMXGPTState), 7667110c3eSJean-Christophe DUBOIS VMSTATE_UINT32(cnt, IMXGPTState), 7767110c3eSJean-Christophe DUBOIS VMSTATE_UINT32(next_timeout, IMXGPTState), 7867110c3eSJean-Christophe DUBOIS VMSTATE_UINT32(next_int, IMXGPTState), 7967110c3eSJean-Christophe DUBOIS VMSTATE_UINT32(freq, IMXGPTState), 8067110c3eSJean-Christophe DUBOIS VMSTATE_PTIMER(timer, IMXGPTState), 81a50c0d6fSJean-Christophe DUBOIS VMSTATE_END_OF_LIST() 82a50c0d6fSJean-Christophe DUBOIS } 83a50c0d6fSJean-Christophe DUBOIS }; 84a50c0d6fSJean-Christophe DUBOIS 8566542f63SJean-Christophe Dubois static const IMXClk imx25_gpt_clocks[] = { 8666542f63SJean-Christophe Dubois CLK_NONE, /* 000 No clock source */ 8766542f63SJean-Christophe Dubois CLK_IPG, /* 001 ipg_clk, 532MHz*/ 8866542f63SJean-Christophe Dubois CLK_IPG_HIGH, /* 010 ipg_clk_highfreq */ 8966542f63SJean-Christophe Dubois CLK_NONE, /* 011 not defined */ 9066542f63SJean-Christophe Dubois CLK_32k, /* 100 ipg_clk_32k */ 9166542f63SJean-Christophe Dubois CLK_32k, /* 101 ipg_clk_32k */ 9266542f63SJean-Christophe Dubois CLK_32k, /* 110 ipg_clk_32k */ 9366542f63SJean-Christophe Dubois CLK_32k, /* 111 ipg_clk_32k */ 9466542f63SJean-Christophe Dubois }; 9566542f63SJean-Christophe Dubois 9666542f63SJean-Christophe Dubois static const IMXClk imx31_gpt_clocks[] = { 97c91a5883SJean-Christophe Dubois CLK_NONE, /* 000 No clock source */ 98aaa9ec3bSJean-Christophe Dubois CLK_IPG, /* 001 ipg_clk, 532MHz*/ 99d552f675SJean-Christophe Dubois CLK_IPG_HIGH, /* 010 ipg_clk_highfreq */ 100c91a5883SJean-Christophe Dubois CLK_NONE, /* 011 not defined */ 101a50c0d6fSJean-Christophe DUBOIS CLK_32k, /* 100 ipg_clk_32k */ 102c91a5883SJean-Christophe Dubois CLK_NONE, /* 101 not defined */ 103c91a5883SJean-Christophe Dubois CLK_NONE, /* 110 not defined */ 104c91a5883SJean-Christophe Dubois CLK_NONE, /* 111 not defined */ 105a50c0d6fSJean-Christophe DUBOIS }; 106a50c0d6fSJean-Christophe DUBOIS 10766542f63SJean-Christophe Dubois static const IMXClk imx6_gpt_clocks[] = { 10866542f63SJean-Christophe Dubois CLK_NONE, /* 000 No clock source */ 10966542f63SJean-Christophe Dubois CLK_IPG, /* 001 ipg_clk, 532MHz*/ 11066542f63SJean-Christophe Dubois CLK_IPG_HIGH, /* 010 ipg_clk_highfreq */ 11166542f63SJean-Christophe Dubois CLK_EXT, /* 011 External clock */ 11266542f63SJean-Christophe Dubois CLK_32k, /* 100 ipg_clk_32k */ 11366542f63SJean-Christophe Dubois CLK_HIGH_DIV, /* 101 reference clock / 8 */ 11466542f63SJean-Christophe Dubois CLK_NONE, /* 110 not defined */ 11566542f63SJean-Christophe Dubois CLK_HIGH, /* 111 reference clock */ 11666542f63SJean-Christophe Dubois }; 11766542f63SJean-Christophe Dubois 118a62bf59fSAndrey Smirnov static const IMXClk imx7_gpt_clocks[] = { 119a62bf59fSAndrey Smirnov CLK_NONE, /* 000 No clock source */ 120a62bf59fSAndrey Smirnov CLK_IPG, /* 001 ipg_clk, 532MHz*/ 121a62bf59fSAndrey Smirnov CLK_IPG_HIGH, /* 010 ipg_clk_highfreq */ 122a62bf59fSAndrey Smirnov CLK_EXT, /* 011 External clock */ 123a62bf59fSAndrey Smirnov CLK_32k, /* 100 ipg_clk_32k */ 124a62bf59fSAndrey Smirnov CLK_HIGH, /* 101 reference clock */ 125a62bf59fSAndrey Smirnov CLK_NONE, /* 110 not defined */ 126a62bf59fSAndrey Smirnov CLK_NONE, /* 111 not defined */ 127a62bf59fSAndrey Smirnov }; 128a62bf59fSAndrey Smirnov 1291b914994SPeter Maydell /* Must be called from within ptimer_transaction_begin/commit block */ 13067110c3eSJean-Christophe DUBOIS static void imx_gpt_set_freq(IMXGPTState *s) 131a50c0d6fSJean-Christophe DUBOIS { 1325ec694b5SJean-Christophe DUBOIS uint32_t clksrc = extract32(s->cr, GPT_CR_CLKSRC_SHIFT, 3); 133a50c0d6fSJean-Christophe DUBOIS 134aaa9ec3bSJean-Christophe Dubois s->freq = imx_ccm_get_clock_frequency(s->ccm, 13566542f63SJean-Christophe Dubois s->clocks[clksrc]) / (1 + s->pr); 136a50c0d6fSJean-Christophe DUBOIS 137aaa9ec3bSJean-Christophe Dubois DPRINTF("Setting clksrc %d to frequency %d\n", clksrc, s->freq); 138aaa9ec3bSJean-Christophe Dubois 139aaa9ec3bSJean-Christophe Dubois if (s->freq) { 140aaa9ec3bSJean-Christophe Dubois ptimer_set_freq(s->timer, s->freq); 141a50c0d6fSJean-Christophe DUBOIS } 142a50c0d6fSJean-Christophe DUBOIS } 143a50c0d6fSJean-Christophe DUBOIS 14467110c3eSJean-Christophe DUBOIS static void imx_gpt_update_int(IMXGPTState *s) 145a50c0d6fSJean-Christophe DUBOIS { 1465ec694b5SJean-Christophe DUBOIS if ((s->sr & s->ir) && (s->cr & GPT_CR_EN)) { 1475ec694b5SJean-Christophe DUBOIS qemu_irq_raise(s->irq); 1485ec694b5SJean-Christophe DUBOIS } else { 1495ec694b5SJean-Christophe DUBOIS qemu_irq_lower(s->irq); 1505ec694b5SJean-Christophe DUBOIS } 151a50c0d6fSJean-Christophe DUBOIS } 152a50c0d6fSJean-Christophe DUBOIS 15367110c3eSJean-Christophe DUBOIS static uint32_t imx_gpt_update_count(IMXGPTState *s) 154a50c0d6fSJean-Christophe DUBOIS { 1555ec694b5SJean-Christophe DUBOIS s->cnt = s->next_timeout - (uint32_t)ptimer_get_count(s->timer); 1565ec694b5SJean-Christophe DUBOIS 157a50c0d6fSJean-Christophe DUBOIS return s->cnt; 158a50c0d6fSJean-Christophe DUBOIS } 159a50c0d6fSJean-Christophe DUBOIS 16067110c3eSJean-Christophe DUBOIS static inline uint32_t imx_gpt_find_limit(uint32_t count, uint32_t reg, 1615ec694b5SJean-Christophe DUBOIS uint32_t timeout) 162a50c0d6fSJean-Christophe DUBOIS { 1635ec694b5SJean-Christophe DUBOIS if ((count < reg) && (timeout > reg)) { 1645ec694b5SJean-Christophe DUBOIS timeout = reg; 1655ec694b5SJean-Christophe DUBOIS } 166a50c0d6fSJean-Christophe DUBOIS 1675ec694b5SJean-Christophe DUBOIS return timeout; 1685ec694b5SJean-Christophe DUBOIS } 1695ec694b5SJean-Christophe DUBOIS 1701b914994SPeter Maydell /* Must be called from within ptimer_transaction_begin/commit block */ 17167110c3eSJean-Christophe DUBOIS static void imx_gpt_compute_next_timeout(IMXGPTState *s, bool event) 1725ec694b5SJean-Christophe DUBOIS { 173203d65a4SMichael Tokarev uint32_t timeout = GPT_TIMER_MAX; 1744833e15fSJean-Christophe Dubois uint32_t count; 1755ec694b5SJean-Christophe DUBOIS long long limit; 1765ec694b5SJean-Christophe DUBOIS 1775ec694b5SJean-Christophe DUBOIS if (!(s->cr & GPT_CR_EN)) { 1785ec694b5SJean-Christophe DUBOIS /* if not enabled just return */ 179a50c0d6fSJean-Christophe DUBOIS return; 180a50c0d6fSJean-Christophe DUBOIS } 181a50c0d6fSJean-Christophe DUBOIS 1824833e15fSJean-Christophe Dubois /* update the count */ 1834833e15fSJean-Christophe Dubois count = imx_gpt_update_count(s); 1844833e15fSJean-Christophe Dubois 1855ec694b5SJean-Christophe DUBOIS if (event) { 186a50c0d6fSJean-Christophe DUBOIS /* 1874833e15fSJean-Christophe Dubois * This is an event (the ptimer reached 0 and stopped), and the 1884833e15fSJean-Christophe Dubois * timer counter is now equal to s->next_timeout. 189a50c0d6fSJean-Christophe DUBOIS */ 1904833e15fSJean-Christophe Dubois if (!(s->cr & GPT_CR_FRR) && (count == s->ocr1)) { 1914833e15fSJean-Christophe Dubois /* We are in restart mode and we crossed the compare channel 1 1924833e15fSJean-Christophe Dubois * value. We need to reset the counter to 0. 1934833e15fSJean-Christophe Dubois */ 1944833e15fSJean-Christophe Dubois count = s->cnt = s->next_timeout = 0; 1954833e15fSJean-Christophe Dubois } else if (count == GPT_TIMER_MAX) { 1964833e15fSJean-Christophe Dubois /* We reached GPT_TIMER_MAX so we need to rollover */ 1974833e15fSJean-Christophe Dubois count = s->cnt = s->next_timeout = 0; 198a50c0d6fSJean-Christophe DUBOIS } 1995ec694b5SJean-Christophe DUBOIS } 2005ec694b5SJean-Christophe DUBOIS 2015ec694b5SJean-Christophe DUBOIS /* now, find the next timeout related to count */ 2025ec694b5SJean-Christophe DUBOIS 2035ec694b5SJean-Christophe DUBOIS if (s->ir & GPT_IR_OF1IE) { 20467110c3eSJean-Christophe DUBOIS timeout = imx_gpt_find_limit(count, s->ocr1, timeout); 2055ec694b5SJean-Christophe DUBOIS } 2065ec694b5SJean-Christophe DUBOIS if (s->ir & GPT_IR_OF2IE) { 20767110c3eSJean-Christophe DUBOIS timeout = imx_gpt_find_limit(count, s->ocr2, timeout); 2085ec694b5SJean-Christophe DUBOIS } 2095ec694b5SJean-Christophe DUBOIS if (s->ir & GPT_IR_OF3IE) { 21067110c3eSJean-Christophe DUBOIS timeout = imx_gpt_find_limit(count, s->ocr3, timeout); 2115ec694b5SJean-Christophe DUBOIS } 2125ec694b5SJean-Christophe DUBOIS 2135ec694b5SJean-Christophe DUBOIS /* find the next set of interrupts to raise for next timer event */ 2145ec694b5SJean-Christophe DUBOIS 2155ec694b5SJean-Christophe DUBOIS s->next_int = 0; 2165ec694b5SJean-Christophe DUBOIS if ((s->ir & GPT_IR_OF1IE) && (timeout == s->ocr1)) { 2175ec694b5SJean-Christophe DUBOIS s->next_int |= GPT_SR_OF1; 2185ec694b5SJean-Christophe DUBOIS } 2195ec694b5SJean-Christophe DUBOIS if ((s->ir & GPT_IR_OF2IE) && (timeout == s->ocr2)) { 2205ec694b5SJean-Christophe DUBOIS s->next_int |= GPT_SR_OF2; 2215ec694b5SJean-Christophe DUBOIS } 2225ec694b5SJean-Christophe DUBOIS if ((s->ir & GPT_IR_OF3IE) && (timeout == s->ocr3)) { 2235ec694b5SJean-Christophe DUBOIS s->next_int |= GPT_SR_OF3; 2245ec694b5SJean-Christophe DUBOIS } 225203d65a4SMichael Tokarev if ((s->ir & GPT_IR_ROVIE) && (timeout == GPT_TIMER_MAX)) { 2265ec694b5SJean-Christophe DUBOIS s->next_int |= GPT_SR_ROV; 2275ec694b5SJean-Christophe DUBOIS } 2285ec694b5SJean-Christophe DUBOIS 2295ec694b5SJean-Christophe DUBOIS /* the new range to count down from */ 23067110c3eSJean-Christophe DUBOIS limit = timeout - imx_gpt_update_count(s); 2315ec694b5SJean-Christophe DUBOIS 2325ec694b5SJean-Christophe DUBOIS if (limit < 0) { 2335ec694b5SJean-Christophe DUBOIS /* 2345ec694b5SJean-Christophe DUBOIS * if we reach here, then QEMU is running too slow and we pass the 2355ec694b5SJean-Christophe DUBOIS * timeout limit while computing it. Let's deliver the interrupt 2365ec694b5SJean-Christophe DUBOIS * and compute a new limit. 2375ec694b5SJean-Christophe DUBOIS */ 2385ec694b5SJean-Christophe DUBOIS s->sr |= s->next_int; 2395ec694b5SJean-Christophe DUBOIS 24067110c3eSJean-Christophe DUBOIS imx_gpt_compute_next_timeout(s, event); 2415ec694b5SJean-Christophe DUBOIS 24267110c3eSJean-Christophe DUBOIS imx_gpt_update_int(s); 2435ec694b5SJean-Christophe DUBOIS } else { 2445ec694b5SJean-Christophe DUBOIS /* New timeout value */ 2455ec694b5SJean-Christophe DUBOIS s->next_timeout = timeout; 2465ec694b5SJean-Christophe DUBOIS 2475ec694b5SJean-Christophe DUBOIS /* reset the limit to the computed range */ 2485ec694b5SJean-Christophe DUBOIS ptimer_set_limit(s->timer, limit, 1); 2495ec694b5SJean-Christophe DUBOIS } 250a50c0d6fSJean-Christophe DUBOIS } 251a50c0d6fSJean-Christophe DUBOIS 25267110c3eSJean-Christophe DUBOIS static uint64_t imx_gpt_read(void *opaque, hwaddr offset, unsigned size) 253a50c0d6fSJean-Christophe DUBOIS { 25467110c3eSJean-Christophe DUBOIS IMXGPTState *s = IMX_GPT(opaque); 2555ec694b5SJean-Christophe DUBOIS uint32_t reg_value = 0; 256a50c0d6fSJean-Christophe DUBOIS 25705453526SJean-Christophe Dubois switch (offset >> 2) { 258a50c0d6fSJean-Christophe DUBOIS case 0: /* Control Register */ 2595ec694b5SJean-Christophe DUBOIS reg_value = s->cr; 2605ec694b5SJean-Christophe DUBOIS break; 261a50c0d6fSJean-Christophe DUBOIS 262a50c0d6fSJean-Christophe DUBOIS case 1: /* prescaler */ 2635ec694b5SJean-Christophe DUBOIS reg_value = s->pr; 2645ec694b5SJean-Christophe DUBOIS break; 265a50c0d6fSJean-Christophe DUBOIS 266a50c0d6fSJean-Christophe DUBOIS case 2: /* Status Register */ 2675ec694b5SJean-Christophe DUBOIS reg_value = s->sr; 2685ec694b5SJean-Christophe DUBOIS break; 269a50c0d6fSJean-Christophe DUBOIS 270a50c0d6fSJean-Christophe DUBOIS case 3: /* Interrupt Register */ 2715ec694b5SJean-Christophe DUBOIS reg_value = s->ir; 2725ec694b5SJean-Christophe DUBOIS break; 273a50c0d6fSJean-Christophe DUBOIS 274a50c0d6fSJean-Christophe DUBOIS case 4: /* Output Compare Register 1 */ 2755ec694b5SJean-Christophe DUBOIS reg_value = s->ocr1; 2765ec694b5SJean-Christophe DUBOIS break; 277a50c0d6fSJean-Christophe DUBOIS 278a50c0d6fSJean-Christophe DUBOIS case 5: /* Output Compare Register 2 */ 2795ec694b5SJean-Christophe DUBOIS reg_value = s->ocr2; 2805ec694b5SJean-Christophe DUBOIS break; 281a50c0d6fSJean-Christophe DUBOIS 282a50c0d6fSJean-Christophe DUBOIS case 6: /* Output Compare Register 3 */ 2835ec694b5SJean-Christophe DUBOIS reg_value = s->ocr3; 2845ec694b5SJean-Christophe DUBOIS break; 285a50c0d6fSJean-Christophe DUBOIS 286a50c0d6fSJean-Christophe DUBOIS case 7: /* input Capture Register 1 */ 28705453526SJean-Christophe Dubois qemu_log_mask(LOG_UNIMP, "[%s]%s: icr1 feature is not implemented\n", 28805453526SJean-Christophe Dubois TYPE_IMX_GPT, __func__); 2895ec694b5SJean-Christophe DUBOIS reg_value = s->icr1; 2905ec694b5SJean-Christophe DUBOIS break; 291a50c0d6fSJean-Christophe DUBOIS 292a50c0d6fSJean-Christophe DUBOIS case 8: /* input Capture Register 2 */ 29305453526SJean-Christophe Dubois qemu_log_mask(LOG_UNIMP, "[%s]%s: icr2 feature is not implemented\n", 29405453526SJean-Christophe Dubois TYPE_IMX_GPT, __func__); 2955ec694b5SJean-Christophe DUBOIS reg_value = s->icr2; 2965ec694b5SJean-Christophe DUBOIS break; 297a50c0d6fSJean-Christophe DUBOIS 298a50c0d6fSJean-Christophe DUBOIS case 9: /* cnt */ 29967110c3eSJean-Christophe DUBOIS imx_gpt_update_count(s); 3005ec694b5SJean-Christophe DUBOIS reg_value = s->cnt; 3015ec694b5SJean-Christophe DUBOIS break; 3025ec694b5SJean-Christophe DUBOIS 3035ec694b5SJean-Christophe DUBOIS default: 30405453526SJean-Christophe Dubois qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" 30505453526SJean-Christophe Dubois HWADDR_PRIx "\n", TYPE_IMX_GPT, __func__, offset); 3065ec694b5SJean-Christophe DUBOIS break; 307a50c0d6fSJean-Christophe DUBOIS } 308a50c0d6fSJean-Christophe DUBOIS 30905453526SJean-Christophe Dubois DPRINTF("(%s) = 0x%08x\n", imx_gpt_reg_name(offset >> 2), reg_value); 310a50c0d6fSJean-Christophe DUBOIS 3115ec694b5SJean-Christophe DUBOIS return reg_value; 312a50c0d6fSJean-Christophe DUBOIS } 313a50c0d6fSJean-Christophe DUBOIS 314a50c0d6fSJean-Christophe DUBOIS 315c98c9ebaSKurban Mallachiev static void imx_gpt_reset_common(IMXGPTState *s, bool is_soft_reset) 316c98c9ebaSKurban Mallachiev { 3171b914994SPeter Maydell ptimer_transaction_begin(s->timer); 3185ec694b5SJean-Christophe DUBOIS /* stop timer */ 3195ec694b5SJean-Christophe DUBOIS ptimer_stop(s->timer); 3205ec694b5SJean-Christophe DUBOIS 321c98c9ebaSKurban Mallachiev /* Soft reset and hard reset differ only in their handling of the CR 322c98c9ebaSKurban Mallachiev * register -- soft reset preserves the values of some bits there. 323a50c0d6fSJean-Christophe DUBOIS */ 324c98c9ebaSKurban Mallachiev if (is_soft_reset) { 325c98c9ebaSKurban Mallachiev /* Clear all CR bits except those that are preserved by soft reset. */ 326c98c9ebaSKurban Mallachiev s->cr &= GPT_CR_EN | GPT_CR_ENMOD | GPT_CR_STOPEN | GPT_CR_DOZEN | 327c98c9ebaSKurban Mallachiev GPT_CR_WAITEN | GPT_CR_DBGEN | 328c98c9ebaSKurban Mallachiev (GPT_CR_CLKSRC_MASK << GPT_CR_CLKSRC_SHIFT); 329c98c9ebaSKurban Mallachiev } else { 330c98c9ebaSKurban Mallachiev s->cr = 0; 331c98c9ebaSKurban Mallachiev } 332a50c0d6fSJean-Christophe DUBOIS s->sr = 0; 333a50c0d6fSJean-Christophe DUBOIS s->pr = 0; 334a50c0d6fSJean-Christophe DUBOIS s->ir = 0; 335a50c0d6fSJean-Christophe DUBOIS s->cnt = 0; 336203d65a4SMichael Tokarev s->ocr1 = GPT_TIMER_MAX; 337203d65a4SMichael Tokarev s->ocr2 = GPT_TIMER_MAX; 338203d65a4SMichael Tokarev s->ocr3 = GPT_TIMER_MAX; 339a50c0d6fSJean-Christophe DUBOIS s->icr1 = 0; 340a50c0d6fSJean-Christophe DUBOIS s->icr2 = 0; 3415ec694b5SJean-Christophe DUBOIS 342203d65a4SMichael Tokarev s->next_timeout = GPT_TIMER_MAX; 3435ec694b5SJean-Christophe DUBOIS s->next_int = 0; 3445ec694b5SJean-Christophe DUBOIS 3455ec694b5SJean-Christophe DUBOIS /* compute new freq */ 34667110c3eSJean-Christophe DUBOIS imx_gpt_set_freq(s); 3475ec694b5SJean-Christophe DUBOIS 348203d65a4SMichael Tokarev /* reset the limit to GPT_TIMER_MAX */ 349203d65a4SMichael Tokarev ptimer_set_limit(s->timer, GPT_TIMER_MAX, 1); 3505ec694b5SJean-Christophe DUBOIS 3515ec694b5SJean-Christophe DUBOIS /* if the timer is still enabled, restart it */ 3525ec694b5SJean-Christophe DUBOIS if (s->freq && (s->cr & GPT_CR_EN)) { 3535ec694b5SJean-Christophe DUBOIS ptimer_run(s->timer, 1); 3545ec694b5SJean-Christophe DUBOIS } 3551b914994SPeter Maydell ptimer_transaction_commit(s->timer); 356a50c0d6fSJean-Christophe DUBOIS } 357a50c0d6fSJean-Christophe DUBOIS 358c98c9ebaSKurban Mallachiev static void imx_gpt_soft_reset(DeviceState *dev) 359c98c9ebaSKurban Mallachiev { 360c98c9ebaSKurban Mallachiev IMXGPTState *s = IMX_GPT(dev); 361c98c9ebaSKurban Mallachiev imx_gpt_reset_common(s, true); 362c98c9ebaSKurban Mallachiev } 363c98c9ebaSKurban Mallachiev 364c98c9ebaSKurban Mallachiev static void imx_gpt_reset(DeviceState *dev) 365c98c9ebaSKurban Mallachiev { 366c98c9ebaSKurban Mallachiev IMXGPTState *s = IMX_GPT(dev); 367c98c9ebaSKurban Mallachiev imx_gpt_reset_common(s, false); 368c98c9ebaSKurban Mallachiev } 369c98c9ebaSKurban Mallachiev 37067110c3eSJean-Christophe DUBOIS static void imx_gpt_write(void *opaque, hwaddr offset, uint64_t value, 37167110c3eSJean-Christophe DUBOIS unsigned size) 372a50c0d6fSJean-Christophe DUBOIS { 37367110c3eSJean-Christophe DUBOIS IMXGPTState *s = IMX_GPT(opaque); 3745ec694b5SJean-Christophe DUBOIS uint32_t oldreg; 375a50c0d6fSJean-Christophe DUBOIS 37605453526SJean-Christophe Dubois DPRINTF("(%s, value = 0x%08x)\n", imx_gpt_reg_name(offset >> 2), 3775ec694b5SJean-Christophe DUBOIS (uint32_t)value); 378a50c0d6fSJean-Christophe DUBOIS 37905453526SJean-Christophe Dubois switch (offset >> 2) { 3805ec694b5SJean-Christophe DUBOIS case 0: 3815ec694b5SJean-Christophe DUBOIS oldreg = s->cr; 3825ec694b5SJean-Christophe DUBOIS s->cr = value & ~0x7c14; 3835ec694b5SJean-Christophe DUBOIS if (s->cr & GPT_CR_SWR) { /* force reset */ 3845ec694b5SJean-Christophe DUBOIS /* handle the reset */ 385c98c9ebaSKurban Mallachiev imx_gpt_soft_reset(DEVICE(s)); 386a50c0d6fSJean-Christophe DUBOIS } else { 3875ec694b5SJean-Christophe DUBOIS /* set our freq, as the source might have changed */ 3881b914994SPeter Maydell ptimer_transaction_begin(s->timer); 38967110c3eSJean-Christophe DUBOIS imx_gpt_set_freq(s); 3905ec694b5SJean-Christophe DUBOIS 3915ec694b5SJean-Christophe DUBOIS if ((oldreg ^ s->cr) & GPT_CR_EN) { 3925ec694b5SJean-Christophe DUBOIS if (s->cr & GPT_CR_EN) { 3935ec694b5SJean-Christophe DUBOIS if (s->cr & GPT_CR_ENMOD) { 394203d65a4SMichael Tokarev s->next_timeout = GPT_TIMER_MAX; 395203d65a4SMichael Tokarev ptimer_set_count(s->timer, GPT_TIMER_MAX); 39667110c3eSJean-Christophe DUBOIS imx_gpt_compute_next_timeout(s, false); 3975ec694b5SJean-Christophe DUBOIS } 3985ec694b5SJean-Christophe DUBOIS ptimer_run(s->timer, 1); 3995ec694b5SJean-Christophe DUBOIS } else { 4005ec694b5SJean-Christophe DUBOIS /* stop timer */ 401a50c0d6fSJean-Christophe DUBOIS ptimer_stop(s->timer); 402a50c0d6fSJean-Christophe DUBOIS } 403a50c0d6fSJean-Christophe DUBOIS } 4041b914994SPeter Maydell ptimer_transaction_commit(s->timer); 4055ec694b5SJean-Christophe DUBOIS } 4065ec694b5SJean-Christophe DUBOIS break; 407a50c0d6fSJean-Christophe DUBOIS 408a50c0d6fSJean-Christophe DUBOIS case 1: /* Prescaler */ 409a50c0d6fSJean-Christophe DUBOIS s->pr = value & 0xfff; 4101b914994SPeter Maydell ptimer_transaction_begin(s->timer); 41167110c3eSJean-Christophe DUBOIS imx_gpt_set_freq(s); 4121b914994SPeter Maydell ptimer_transaction_commit(s->timer); 4135ec694b5SJean-Christophe DUBOIS break; 414a50c0d6fSJean-Christophe DUBOIS 415a50c0d6fSJean-Christophe DUBOIS case 2: /* SR */ 4165ec694b5SJean-Christophe DUBOIS s->sr &= ~(value & 0x3f); 41767110c3eSJean-Christophe DUBOIS imx_gpt_update_int(s); 4185ec694b5SJean-Christophe DUBOIS break; 419a50c0d6fSJean-Christophe DUBOIS 420a50c0d6fSJean-Christophe DUBOIS case 3: /* IR -- interrupt register */ 421a50c0d6fSJean-Christophe DUBOIS s->ir = value & 0x3f; 42267110c3eSJean-Christophe DUBOIS imx_gpt_update_int(s); 4235ec694b5SJean-Christophe DUBOIS 4241b914994SPeter Maydell ptimer_transaction_begin(s->timer); 42567110c3eSJean-Christophe DUBOIS imx_gpt_compute_next_timeout(s, false); 4261b914994SPeter Maydell ptimer_transaction_commit(s->timer); 4275ec694b5SJean-Christophe DUBOIS 4285ec694b5SJean-Christophe DUBOIS break; 429a50c0d6fSJean-Christophe DUBOIS 430a50c0d6fSJean-Christophe DUBOIS case 4: /* OCR1 -- output compare register */ 4315ec694b5SJean-Christophe DUBOIS s->ocr1 = value; 4325ec694b5SJean-Christophe DUBOIS 4331b914994SPeter Maydell ptimer_transaction_begin(s->timer); 434a50c0d6fSJean-Christophe DUBOIS /* In non-freerun mode, reset count when this register is written */ 435a50c0d6fSJean-Christophe DUBOIS if (!(s->cr & GPT_CR_FRR)) { 436203d65a4SMichael Tokarev s->next_timeout = GPT_TIMER_MAX; 437203d65a4SMichael Tokarev ptimer_set_limit(s->timer, GPT_TIMER_MAX, 1); 438a50c0d6fSJean-Christophe DUBOIS } 4395ec694b5SJean-Christophe DUBOIS 4405ec694b5SJean-Christophe DUBOIS /* compute the new timeout */ 44167110c3eSJean-Christophe DUBOIS imx_gpt_compute_next_timeout(s, false); 4421b914994SPeter Maydell ptimer_transaction_commit(s->timer); 4435ec694b5SJean-Christophe DUBOIS 4445ec694b5SJean-Christophe DUBOIS break; 445a50c0d6fSJean-Christophe DUBOIS 446a50c0d6fSJean-Christophe DUBOIS case 5: /* OCR2 -- output compare register */ 4475ec694b5SJean-Christophe DUBOIS s->ocr2 = value; 4485ec694b5SJean-Christophe DUBOIS 4495ec694b5SJean-Christophe DUBOIS /* compute the new timeout */ 4501b914994SPeter Maydell ptimer_transaction_begin(s->timer); 45167110c3eSJean-Christophe DUBOIS imx_gpt_compute_next_timeout(s, false); 4521b914994SPeter Maydell ptimer_transaction_commit(s->timer); 4535ec694b5SJean-Christophe DUBOIS 4545ec694b5SJean-Christophe DUBOIS break; 4555ec694b5SJean-Christophe DUBOIS 456a50c0d6fSJean-Christophe DUBOIS case 6: /* OCR3 -- output compare register */ 4575ec694b5SJean-Christophe DUBOIS s->ocr3 = value; 4585ec694b5SJean-Christophe DUBOIS 4595ec694b5SJean-Christophe DUBOIS /* compute the new timeout */ 4601b914994SPeter Maydell ptimer_transaction_begin(s->timer); 46167110c3eSJean-Christophe DUBOIS imx_gpt_compute_next_timeout(s, false); 4621b914994SPeter Maydell ptimer_transaction_commit(s->timer); 4635ec694b5SJean-Christophe DUBOIS 4645ec694b5SJean-Christophe DUBOIS break; 4655ec694b5SJean-Christophe DUBOIS 466a50c0d6fSJean-Christophe DUBOIS default: 46705453526SJean-Christophe Dubois qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" 46805453526SJean-Christophe Dubois HWADDR_PRIx "\n", TYPE_IMX_GPT, __func__, offset); 4695ec694b5SJean-Christophe DUBOIS break; 470a50c0d6fSJean-Christophe DUBOIS } 471a50c0d6fSJean-Christophe DUBOIS } 472a50c0d6fSJean-Christophe DUBOIS 47367110c3eSJean-Christophe DUBOIS static void imx_gpt_timeout(void *opaque) 474a50c0d6fSJean-Christophe DUBOIS { 47567110c3eSJean-Christophe DUBOIS IMXGPTState *s = IMX_GPT(opaque); 476a50c0d6fSJean-Christophe DUBOIS 4775ec694b5SJean-Christophe DUBOIS DPRINTF("\n"); 478a50c0d6fSJean-Christophe DUBOIS 4795ec694b5SJean-Christophe DUBOIS s->sr |= s->next_int; 4805ec694b5SJean-Christophe DUBOIS s->next_int = 0; 481a50c0d6fSJean-Christophe DUBOIS 48267110c3eSJean-Christophe DUBOIS imx_gpt_compute_next_timeout(s, true); 4835ec694b5SJean-Christophe DUBOIS 48467110c3eSJean-Christophe DUBOIS imx_gpt_update_int(s); 4855ec694b5SJean-Christophe DUBOIS 4865ec694b5SJean-Christophe DUBOIS if (s->freq && (s->cr & GPT_CR_EN)) { 4875ec694b5SJean-Christophe DUBOIS ptimer_run(s->timer, 1); 4885ec694b5SJean-Christophe DUBOIS } 489a50c0d6fSJean-Christophe DUBOIS } 490a50c0d6fSJean-Christophe DUBOIS 49167110c3eSJean-Christophe DUBOIS static const MemoryRegionOps imx_gpt_ops = { 49267110c3eSJean-Christophe DUBOIS .read = imx_gpt_read, 49367110c3eSJean-Christophe DUBOIS .write = imx_gpt_write, 494a50c0d6fSJean-Christophe DUBOIS .endianness = DEVICE_NATIVE_ENDIAN, 495a50c0d6fSJean-Christophe DUBOIS }; 496a50c0d6fSJean-Christophe DUBOIS 497a50c0d6fSJean-Christophe DUBOIS 49867110c3eSJean-Christophe DUBOIS static void imx_gpt_realize(DeviceState *dev, Error **errp) 499a50c0d6fSJean-Christophe DUBOIS { 50067110c3eSJean-Christophe DUBOIS IMXGPTState *s = IMX_GPT(dev); 50167110c3eSJean-Christophe DUBOIS SysBusDevice *sbd = SYS_BUS_DEVICE(dev); 502a50c0d6fSJean-Christophe DUBOIS 50367110c3eSJean-Christophe DUBOIS sysbus_init_irq(sbd, &s->irq); 504853dca12SPaolo Bonzini memory_region_init_io(&s->iomem, OBJECT(s), &imx_gpt_ops, s, TYPE_IMX_GPT, 505a50c0d6fSJean-Christophe DUBOIS 0x00001000); 50667110c3eSJean-Christophe DUBOIS sysbus_init_mmio(sbd, &s->iomem); 507a50c0d6fSJean-Christophe DUBOIS 508*9598c1bbSPeter Maydell s->timer = ptimer_init(imx_gpt_timeout, s, PTIMER_POLICY_LEGACY); 509a50c0d6fSJean-Christophe DUBOIS } 510a50c0d6fSJean-Christophe DUBOIS 51167110c3eSJean-Christophe DUBOIS static void imx_gpt_class_init(ObjectClass *klass, void *data) 512a50c0d6fSJean-Christophe DUBOIS { 513a50c0d6fSJean-Christophe DUBOIS DeviceClass *dc = DEVICE_CLASS(klass); 51467110c3eSJean-Christophe DUBOIS 51567110c3eSJean-Christophe DUBOIS dc->realize = imx_gpt_realize; 51667110c3eSJean-Christophe DUBOIS dc->reset = imx_gpt_reset; 51767110c3eSJean-Christophe DUBOIS dc->vmsd = &vmstate_imx_timer_gpt; 518a50c0d6fSJean-Christophe DUBOIS dc->desc = "i.MX general timer"; 519a50c0d6fSJean-Christophe DUBOIS } 520a50c0d6fSJean-Christophe DUBOIS 52166542f63SJean-Christophe Dubois static void imx25_gpt_init(Object *obj) 52266542f63SJean-Christophe Dubois { 52366542f63SJean-Christophe Dubois IMXGPTState *s = IMX_GPT(obj); 52466542f63SJean-Christophe Dubois 52566542f63SJean-Christophe Dubois s->clocks = imx25_gpt_clocks; 52666542f63SJean-Christophe Dubois } 52766542f63SJean-Christophe Dubois 52866542f63SJean-Christophe Dubois static void imx31_gpt_init(Object *obj) 52966542f63SJean-Christophe Dubois { 53066542f63SJean-Christophe Dubois IMXGPTState *s = IMX_GPT(obj); 53166542f63SJean-Christophe Dubois 53266542f63SJean-Christophe Dubois s->clocks = imx31_gpt_clocks; 53366542f63SJean-Christophe Dubois } 53466542f63SJean-Christophe Dubois 53566542f63SJean-Christophe Dubois static void imx6_gpt_init(Object *obj) 53666542f63SJean-Christophe Dubois { 53766542f63SJean-Christophe Dubois IMXGPTState *s = IMX_GPT(obj); 53866542f63SJean-Christophe Dubois 53966542f63SJean-Christophe Dubois s->clocks = imx6_gpt_clocks; 54066542f63SJean-Christophe Dubois } 54166542f63SJean-Christophe Dubois 542a62bf59fSAndrey Smirnov static void imx7_gpt_init(Object *obj) 543a62bf59fSAndrey Smirnov { 544a62bf59fSAndrey Smirnov IMXGPTState *s = IMX_GPT(obj); 545a62bf59fSAndrey Smirnov 546a62bf59fSAndrey Smirnov s->clocks = imx7_gpt_clocks; 547a62bf59fSAndrey Smirnov } 548a62bf59fSAndrey Smirnov 54966542f63SJean-Christophe Dubois static const TypeInfo imx25_gpt_info = { 55066542f63SJean-Christophe Dubois .name = TYPE_IMX25_GPT, 551a50c0d6fSJean-Christophe DUBOIS .parent = TYPE_SYS_BUS_DEVICE, 55267110c3eSJean-Christophe DUBOIS .instance_size = sizeof(IMXGPTState), 55366542f63SJean-Christophe Dubois .instance_init = imx25_gpt_init, 55467110c3eSJean-Christophe DUBOIS .class_init = imx_gpt_class_init, 555a50c0d6fSJean-Christophe DUBOIS }; 556a50c0d6fSJean-Christophe DUBOIS 55766542f63SJean-Christophe Dubois static const TypeInfo imx31_gpt_info = { 55866542f63SJean-Christophe Dubois .name = TYPE_IMX31_GPT, 55966542f63SJean-Christophe Dubois .parent = TYPE_IMX25_GPT, 56066542f63SJean-Christophe Dubois .instance_init = imx31_gpt_init, 56166542f63SJean-Christophe Dubois }; 56266542f63SJean-Christophe Dubois 56366542f63SJean-Christophe Dubois static const TypeInfo imx6_gpt_info = { 56466542f63SJean-Christophe Dubois .name = TYPE_IMX6_GPT, 56566542f63SJean-Christophe Dubois .parent = TYPE_IMX25_GPT, 56666542f63SJean-Christophe Dubois .instance_init = imx6_gpt_init, 56766542f63SJean-Christophe Dubois }; 56866542f63SJean-Christophe Dubois 569a62bf59fSAndrey Smirnov static const TypeInfo imx7_gpt_info = { 570a62bf59fSAndrey Smirnov .name = TYPE_IMX7_GPT, 571a62bf59fSAndrey Smirnov .parent = TYPE_IMX25_GPT, 572a62bf59fSAndrey Smirnov .instance_init = imx7_gpt_init, 573a62bf59fSAndrey Smirnov }; 574a62bf59fSAndrey Smirnov 57567110c3eSJean-Christophe DUBOIS static void imx_gpt_register_types(void) 576a50c0d6fSJean-Christophe DUBOIS { 57766542f63SJean-Christophe Dubois type_register_static(&imx25_gpt_info); 57866542f63SJean-Christophe Dubois type_register_static(&imx31_gpt_info); 57966542f63SJean-Christophe Dubois type_register_static(&imx6_gpt_info); 580a62bf59fSAndrey Smirnov type_register_static(&imx7_gpt_info); 581a50c0d6fSJean-Christophe DUBOIS } 582a50c0d6fSJean-Christophe DUBOIS 58367110c3eSJean-Christophe DUBOIS type_init(imx_gpt_register_types) 584