xref: /openbmc/qemu/hw/timer/omap_gptimer.c (revision 77a8257e)
13bd88451SPaolo Bonzini /*
23bd88451SPaolo Bonzini  * TI OMAP2 general purpose timers emulation.
33bd88451SPaolo Bonzini  *
43bd88451SPaolo Bonzini  * Copyright (C) 2007-2008 Nokia Corporation
53bd88451SPaolo Bonzini  * Written by Andrzej Zaborowski <andrew@openedhand.com>
63bd88451SPaolo Bonzini  *
73bd88451SPaolo Bonzini  * This program is free software; you can redistribute it and/or
83bd88451SPaolo Bonzini  * modify it under the terms of the GNU General Public License as
93bd88451SPaolo Bonzini  * published by the Free Software Foundation; either version 2 or
103bd88451SPaolo Bonzini  * (at your option) any later version of the License.
113bd88451SPaolo Bonzini  *
123bd88451SPaolo Bonzini  * This program is distributed in the hope that it will be useful,
133bd88451SPaolo Bonzini  * but WITHOUT ANY WARRANTY; without even the implied warranty of
143bd88451SPaolo Bonzini  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
153bd88451SPaolo Bonzini  * GNU General Public License for more details.
163bd88451SPaolo Bonzini  *
173bd88451SPaolo Bonzini  * You should have received a copy of the GNU General Public License along
183bd88451SPaolo Bonzini  * with this program; if not, see <http://www.gnu.org/licenses/>.
193bd88451SPaolo Bonzini  */
203bd88451SPaolo Bonzini #include "hw/hw.h"
213bd88451SPaolo Bonzini #include "qemu/timer.h"
223bd88451SPaolo Bonzini #include "hw/arm/omap.h"
233bd88451SPaolo Bonzini 
243bd88451SPaolo Bonzini /* GP timers */
253bd88451SPaolo Bonzini struct omap_gp_timer_s {
263bd88451SPaolo Bonzini     MemoryRegion iomem;
273bd88451SPaolo Bonzini     qemu_irq irq;
283bd88451SPaolo Bonzini     qemu_irq wkup;
293bd88451SPaolo Bonzini     qemu_irq in;
303bd88451SPaolo Bonzini     qemu_irq out;
313bd88451SPaolo Bonzini     omap_clk clk;
323bd88451SPaolo Bonzini     QEMUTimer *timer;
333bd88451SPaolo Bonzini     QEMUTimer *match;
343bd88451SPaolo Bonzini     struct omap_target_agent_s *ta;
353bd88451SPaolo Bonzini 
363bd88451SPaolo Bonzini     int in_val;
373bd88451SPaolo Bonzini     int out_val;
383bd88451SPaolo Bonzini     int64_t time;
393bd88451SPaolo Bonzini     int64_t rate;
403bd88451SPaolo Bonzini     int64_t ticks_per_sec;
413bd88451SPaolo Bonzini 
423bd88451SPaolo Bonzini     int16_t config;
433bd88451SPaolo Bonzini     int status;
443bd88451SPaolo Bonzini     int it_ena;
453bd88451SPaolo Bonzini     int wu_ena;
463bd88451SPaolo Bonzini     int enable;
473bd88451SPaolo Bonzini     int inout;
483bd88451SPaolo Bonzini     int capt2;
493bd88451SPaolo Bonzini     int pt;
503bd88451SPaolo Bonzini     enum {
513bd88451SPaolo Bonzini         gpt_trigger_none, gpt_trigger_overflow, gpt_trigger_both
523bd88451SPaolo Bonzini     } trigger;
533bd88451SPaolo Bonzini     enum {
543bd88451SPaolo Bonzini         gpt_capture_none, gpt_capture_rising,
553bd88451SPaolo Bonzini         gpt_capture_falling, gpt_capture_both
563bd88451SPaolo Bonzini     } capture;
573bd88451SPaolo Bonzini     int scpwm;
583bd88451SPaolo Bonzini     int ce;
593bd88451SPaolo Bonzini     int pre;
603bd88451SPaolo Bonzini     int ptv;
613bd88451SPaolo Bonzini     int ar;
623bd88451SPaolo Bonzini     int st;
633bd88451SPaolo Bonzini     int posted;
643bd88451SPaolo Bonzini     uint32_t val;
653bd88451SPaolo Bonzini     uint32_t load_val;
663bd88451SPaolo Bonzini     uint32_t capture_val[2];
673bd88451SPaolo Bonzini     uint32_t match_val;
683bd88451SPaolo Bonzini     int capt_num;
693bd88451SPaolo Bonzini 
703bd88451SPaolo Bonzini     uint16_t writeh;	/* LSB */
713bd88451SPaolo Bonzini     uint16_t readh;	/* MSB */
723bd88451SPaolo Bonzini };
733bd88451SPaolo Bonzini 
743bd88451SPaolo Bonzini #define GPT_TCAR_IT	(1 << 2)
753bd88451SPaolo Bonzini #define GPT_OVF_IT	(1 << 1)
763bd88451SPaolo Bonzini #define GPT_MAT_IT	(1 << 0)
773bd88451SPaolo Bonzini 
783bd88451SPaolo Bonzini static inline void omap_gp_timer_intr(struct omap_gp_timer_s *timer, int it)
793bd88451SPaolo Bonzini {
803bd88451SPaolo Bonzini     if (timer->it_ena & it) {
813bd88451SPaolo Bonzini         if (!timer->status)
823bd88451SPaolo Bonzini             qemu_irq_raise(timer->irq);
833bd88451SPaolo Bonzini 
843bd88451SPaolo Bonzini         timer->status |= it;
853bd88451SPaolo Bonzini         /* Or are the status bits set even when masked?
863bd88451SPaolo Bonzini          * i.e. is masking applied before or after the status register?  */
873bd88451SPaolo Bonzini     }
883bd88451SPaolo Bonzini 
893bd88451SPaolo Bonzini     if (timer->wu_ena & it)
903bd88451SPaolo Bonzini         qemu_irq_pulse(timer->wkup);
913bd88451SPaolo Bonzini }
923bd88451SPaolo Bonzini 
933bd88451SPaolo Bonzini static inline void omap_gp_timer_out(struct omap_gp_timer_s *timer, int level)
943bd88451SPaolo Bonzini {
953bd88451SPaolo Bonzini     if (!timer->inout && timer->out_val != level) {
963bd88451SPaolo Bonzini         timer->out_val = level;
973bd88451SPaolo Bonzini         qemu_set_irq(timer->out, level);
983bd88451SPaolo Bonzini     }
993bd88451SPaolo Bonzini }
1003bd88451SPaolo Bonzini 
1013bd88451SPaolo Bonzini static inline uint32_t omap_gp_timer_read(struct omap_gp_timer_s *timer)
1023bd88451SPaolo Bonzini {
1033bd88451SPaolo Bonzini     uint64_t distance;
1043bd88451SPaolo Bonzini 
1053bd88451SPaolo Bonzini     if (timer->st && timer->rate) {
106bc72ad67SAlex Bligh         distance = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - timer->time;
1073bd88451SPaolo Bonzini         distance = muldiv64(distance, timer->rate, timer->ticks_per_sec);
1083bd88451SPaolo Bonzini 
1093bd88451SPaolo Bonzini         if (distance >= 0xffffffff - timer->val)
1103bd88451SPaolo Bonzini             return 0xffffffff;
1113bd88451SPaolo Bonzini         else
1123bd88451SPaolo Bonzini             return timer->val + distance;
1133bd88451SPaolo Bonzini     } else
1143bd88451SPaolo Bonzini         return timer->val;
1153bd88451SPaolo Bonzini }
1163bd88451SPaolo Bonzini 
1173bd88451SPaolo Bonzini static inline void omap_gp_timer_sync(struct omap_gp_timer_s *timer)
1183bd88451SPaolo Bonzini {
1193bd88451SPaolo Bonzini     if (timer->st) {
1203bd88451SPaolo Bonzini         timer->val = omap_gp_timer_read(timer);
121bc72ad67SAlex Bligh         timer->time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
1223bd88451SPaolo Bonzini     }
1233bd88451SPaolo Bonzini }
1243bd88451SPaolo Bonzini 
1253bd88451SPaolo Bonzini static inline void omap_gp_timer_update(struct omap_gp_timer_s *timer)
1263bd88451SPaolo Bonzini {
1273bd88451SPaolo Bonzini     int64_t expires, matches;
1283bd88451SPaolo Bonzini 
1293bd88451SPaolo Bonzini     if (timer->st && timer->rate) {
1303bd88451SPaolo Bonzini         expires = muldiv64(0x100000000ll - timer->val,
1313bd88451SPaolo Bonzini                         timer->ticks_per_sec, timer->rate);
132bc72ad67SAlex Bligh         timer_mod(timer->timer, timer->time + expires);
1333bd88451SPaolo Bonzini 
1343bd88451SPaolo Bonzini         if (timer->ce && timer->match_val >= timer->val) {
1353bd88451SPaolo Bonzini             matches = muldiv64(timer->match_val - timer->val,
1363bd88451SPaolo Bonzini                             timer->ticks_per_sec, timer->rate);
137bc72ad67SAlex Bligh             timer_mod(timer->match, timer->time + matches);
1383bd88451SPaolo Bonzini         } else
139bc72ad67SAlex Bligh             timer_del(timer->match);
1403bd88451SPaolo Bonzini     } else {
141bc72ad67SAlex Bligh         timer_del(timer->timer);
142bc72ad67SAlex Bligh         timer_del(timer->match);
1433bd88451SPaolo Bonzini         omap_gp_timer_out(timer, timer->scpwm);
1443bd88451SPaolo Bonzini     }
1453bd88451SPaolo Bonzini }
1463bd88451SPaolo Bonzini 
1473bd88451SPaolo Bonzini static inline void omap_gp_timer_trigger(struct omap_gp_timer_s *timer)
1483bd88451SPaolo Bonzini {
1493bd88451SPaolo Bonzini     if (timer->pt)
1503bd88451SPaolo Bonzini         /* TODO in overflow-and-match mode if the first event to
1513bd88451SPaolo Bonzini          * occur is the match, don't toggle.  */
1523bd88451SPaolo Bonzini         omap_gp_timer_out(timer, !timer->out_val);
1533bd88451SPaolo Bonzini     else
1543bd88451SPaolo Bonzini         /* TODO inverted pulse on timer->out_val == 1?  */
1553bd88451SPaolo Bonzini         qemu_irq_pulse(timer->out);
1563bd88451SPaolo Bonzini }
1573bd88451SPaolo Bonzini 
1583bd88451SPaolo Bonzini static void omap_gp_timer_tick(void *opaque)
1593bd88451SPaolo Bonzini {
1603bd88451SPaolo Bonzini     struct omap_gp_timer_s *timer = (struct omap_gp_timer_s *) opaque;
1613bd88451SPaolo Bonzini 
1623bd88451SPaolo Bonzini     if (!timer->ar) {
1633bd88451SPaolo Bonzini         timer->st = 0;
1643bd88451SPaolo Bonzini         timer->val = 0;
1653bd88451SPaolo Bonzini     } else {
1663bd88451SPaolo Bonzini         timer->val = timer->load_val;
167bc72ad67SAlex Bligh         timer->time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
1683bd88451SPaolo Bonzini     }
1693bd88451SPaolo Bonzini 
1703bd88451SPaolo Bonzini     if (timer->trigger == gpt_trigger_overflow ||
1713bd88451SPaolo Bonzini                     timer->trigger == gpt_trigger_both)
1723bd88451SPaolo Bonzini         omap_gp_timer_trigger(timer);
1733bd88451SPaolo Bonzini 
1743bd88451SPaolo Bonzini     omap_gp_timer_intr(timer, GPT_OVF_IT);
1753bd88451SPaolo Bonzini     omap_gp_timer_update(timer);
1763bd88451SPaolo Bonzini }
1773bd88451SPaolo Bonzini 
1783bd88451SPaolo Bonzini static void omap_gp_timer_match(void *opaque)
1793bd88451SPaolo Bonzini {
1803bd88451SPaolo Bonzini     struct omap_gp_timer_s *timer = (struct omap_gp_timer_s *) opaque;
1813bd88451SPaolo Bonzini 
1823bd88451SPaolo Bonzini     if (timer->trigger == gpt_trigger_both)
1833bd88451SPaolo Bonzini         omap_gp_timer_trigger(timer);
1843bd88451SPaolo Bonzini 
1853bd88451SPaolo Bonzini     omap_gp_timer_intr(timer, GPT_MAT_IT);
1863bd88451SPaolo Bonzini }
1873bd88451SPaolo Bonzini 
1883bd88451SPaolo Bonzini static void omap_gp_timer_input(void *opaque, int line, int on)
1893bd88451SPaolo Bonzini {
1903bd88451SPaolo Bonzini     struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque;
1913bd88451SPaolo Bonzini     int trigger;
1923bd88451SPaolo Bonzini 
1933bd88451SPaolo Bonzini     switch (s->capture) {
1943bd88451SPaolo Bonzini     default:
1953bd88451SPaolo Bonzini     case gpt_capture_none:
1963bd88451SPaolo Bonzini         trigger = 0;
1973bd88451SPaolo Bonzini         break;
1983bd88451SPaolo Bonzini     case gpt_capture_rising:
1993bd88451SPaolo Bonzini         trigger = !s->in_val && on;
2003bd88451SPaolo Bonzini         break;
2013bd88451SPaolo Bonzini     case gpt_capture_falling:
2023bd88451SPaolo Bonzini         trigger = s->in_val && !on;
2033bd88451SPaolo Bonzini         break;
2043bd88451SPaolo Bonzini     case gpt_capture_both:
2053bd88451SPaolo Bonzini         trigger = (s->in_val == !on);
2063bd88451SPaolo Bonzini         break;
2073bd88451SPaolo Bonzini     }
2083bd88451SPaolo Bonzini     s->in_val = on;
2093bd88451SPaolo Bonzini 
2103bd88451SPaolo Bonzini     if (s->inout && trigger && s->capt_num < 2) {
2113bd88451SPaolo Bonzini         s->capture_val[s->capt_num] = omap_gp_timer_read(s);
2123bd88451SPaolo Bonzini 
2133bd88451SPaolo Bonzini         if (s->capt2 == s->capt_num ++)
2143bd88451SPaolo Bonzini             omap_gp_timer_intr(s, GPT_TCAR_IT);
2153bd88451SPaolo Bonzini     }
2163bd88451SPaolo Bonzini }
2173bd88451SPaolo Bonzini 
2183bd88451SPaolo Bonzini static void omap_gp_timer_clk_update(void *opaque, int line, int on)
2193bd88451SPaolo Bonzini {
2203bd88451SPaolo Bonzini     struct omap_gp_timer_s *timer = (struct omap_gp_timer_s *) opaque;
2213bd88451SPaolo Bonzini 
2223bd88451SPaolo Bonzini     omap_gp_timer_sync(timer);
2233bd88451SPaolo Bonzini     timer->rate = on ? omap_clk_getrate(timer->clk) : 0;
2243bd88451SPaolo Bonzini     omap_gp_timer_update(timer);
2253bd88451SPaolo Bonzini }
2263bd88451SPaolo Bonzini 
2273bd88451SPaolo Bonzini static void omap_gp_timer_clk_setup(struct omap_gp_timer_s *timer)
2283bd88451SPaolo Bonzini {
2293bd88451SPaolo Bonzini     omap_clk_adduser(timer->clk,
230f3c7d038SAndreas Färber                      qemu_allocate_irq(omap_gp_timer_clk_update, timer, 0));
2313bd88451SPaolo Bonzini     timer->rate = omap_clk_getrate(timer->clk);
2323bd88451SPaolo Bonzini }
2333bd88451SPaolo Bonzini 
2343bd88451SPaolo Bonzini void omap_gp_timer_reset(struct omap_gp_timer_s *s)
2353bd88451SPaolo Bonzini {
2363bd88451SPaolo Bonzini     s->config = 0x000;
2373bd88451SPaolo Bonzini     s->status = 0;
2383bd88451SPaolo Bonzini     s->it_ena = 0;
2393bd88451SPaolo Bonzini     s->wu_ena = 0;
2403bd88451SPaolo Bonzini     s->inout = 0;
2413bd88451SPaolo Bonzini     s->capt2 = 0;
2423bd88451SPaolo Bonzini     s->capt_num = 0;
2433bd88451SPaolo Bonzini     s->pt = 0;
2443bd88451SPaolo Bonzini     s->trigger = gpt_trigger_none;
2453bd88451SPaolo Bonzini     s->capture = gpt_capture_none;
2463bd88451SPaolo Bonzini     s->scpwm = 0;
2473bd88451SPaolo Bonzini     s->ce = 0;
2483bd88451SPaolo Bonzini     s->pre = 0;
2493bd88451SPaolo Bonzini     s->ptv = 0;
2503bd88451SPaolo Bonzini     s->ar = 0;
2513bd88451SPaolo Bonzini     s->st = 0;
2523bd88451SPaolo Bonzini     s->posted = 1;
2533bd88451SPaolo Bonzini     s->val = 0x00000000;
2543bd88451SPaolo Bonzini     s->load_val = 0x00000000;
2553bd88451SPaolo Bonzini     s->capture_val[0] = 0x00000000;
2563bd88451SPaolo Bonzini     s->capture_val[1] = 0x00000000;
2573bd88451SPaolo Bonzini     s->match_val = 0x00000000;
2583bd88451SPaolo Bonzini     omap_gp_timer_update(s);
2593bd88451SPaolo Bonzini }
2603bd88451SPaolo Bonzini 
2613bd88451SPaolo Bonzini static uint32_t omap_gp_timer_readw(void *opaque, hwaddr addr)
2623bd88451SPaolo Bonzini {
2633bd88451SPaolo Bonzini     struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque;
2643bd88451SPaolo Bonzini 
2653bd88451SPaolo Bonzini     switch (addr) {
2663bd88451SPaolo Bonzini     case 0x00:	/* TIDR */
2673bd88451SPaolo Bonzini         return 0x21;
2683bd88451SPaolo Bonzini 
2693bd88451SPaolo Bonzini     case 0x10:	/* TIOCP_CFG */
2703bd88451SPaolo Bonzini         return s->config;
2713bd88451SPaolo Bonzini 
2723bd88451SPaolo Bonzini     case 0x14:	/* TISTAT */
2733bd88451SPaolo Bonzini         /* ??? When's this bit reset? */
2743bd88451SPaolo Bonzini         return 1;						/* RESETDONE */
2753bd88451SPaolo Bonzini 
2763bd88451SPaolo Bonzini     case 0x18:	/* TISR */
2773bd88451SPaolo Bonzini         return s->status;
2783bd88451SPaolo Bonzini 
2793bd88451SPaolo Bonzini     case 0x1c:	/* TIER */
2803bd88451SPaolo Bonzini         return s->it_ena;
2813bd88451SPaolo Bonzini 
2823bd88451SPaolo Bonzini     case 0x20:	/* TWER */
2833bd88451SPaolo Bonzini         return s->wu_ena;
2843bd88451SPaolo Bonzini 
2853bd88451SPaolo Bonzini     case 0x24:	/* TCLR */
2863bd88451SPaolo Bonzini         return (s->inout << 14) |
2873bd88451SPaolo Bonzini                 (s->capt2 << 13) |
2883bd88451SPaolo Bonzini                 (s->pt << 12) |
2893bd88451SPaolo Bonzini                 (s->trigger << 10) |
2903bd88451SPaolo Bonzini                 (s->capture << 8) |
2913bd88451SPaolo Bonzini                 (s->scpwm << 7) |
2923bd88451SPaolo Bonzini                 (s->ce << 6) |
2933bd88451SPaolo Bonzini                 (s->pre << 5) |
2943bd88451SPaolo Bonzini                 (s->ptv << 2) |
2953bd88451SPaolo Bonzini                 (s->ar << 1) |
2963bd88451SPaolo Bonzini                 (s->st << 0);
2973bd88451SPaolo Bonzini 
2983bd88451SPaolo Bonzini     case 0x28:	/* TCRR */
2993bd88451SPaolo Bonzini         return omap_gp_timer_read(s);
3003bd88451SPaolo Bonzini 
3013bd88451SPaolo Bonzini     case 0x2c:	/* TLDR */
3023bd88451SPaolo Bonzini         return s->load_val;
3033bd88451SPaolo Bonzini 
3043bd88451SPaolo Bonzini     case 0x30:	/* TTGR */
3053bd88451SPaolo Bonzini         return 0xffffffff;
3063bd88451SPaolo Bonzini 
3073bd88451SPaolo Bonzini     case 0x34:	/* TWPS */
3083bd88451SPaolo Bonzini         return 0x00000000;	/* No posted writes pending.  */
3093bd88451SPaolo Bonzini 
3103bd88451SPaolo Bonzini     case 0x38:	/* TMAR */
3113bd88451SPaolo Bonzini         return s->match_val;
3123bd88451SPaolo Bonzini 
3133bd88451SPaolo Bonzini     case 0x3c:	/* TCAR1 */
3143bd88451SPaolo Bonzini         return s->capture_val[0];
3153bd88451SPaolo Bonzini 
3163bd88451SPaolo Bonzini     case 0x40:	/* TSICR */
3173bd88451SPaolo Bonzini         return s->posted << 2;
3183bd88451SPaolo Bonzini 
3193bd88451SPaolo Bonzini     case 0x44:	/* TCAR2 */
3203bd88451SPaolo Bonzini         return s->capture_val[1];
3213bd88451SPaolo Bonzini     }
3223bd88451SPaolo Bonzini 
3233bd88451SPaolo Bonzini     OMAP_BAD_REG(addr);
3243bd88451SPaolo Bonzini     return 0;
3253bd88451SPaolo Bonzini }
3263bd88451SPaolo Bonzini 
3273bd88451SPaolo Bonzini static uint32_t omap_gp_timer_readh(void *opaque, hwaddr addr)
3283bd88451SPaolo Bonzini {
3293bd88451SPaolo Bonzini     struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque;
3303bd88451SPaolo Bonzini     uint32_t ret;
3313bd88451SPaolo Bonzini 
3323bd88451SPaolo Bonzini     if (addr & 2)
3333bd88451SPaolo Bonzini         return s->readh;
3343bd88451SPaolo Bonzini     else {
3353bd88451SPaolo Bonzini         ret = omap_gp_timer_readw(opaque, addr);
3363bd88451SPaolo Bonzini         s->readh = ret >> 16;
3373bd88451SPaolo Bonzini         return ret & 0xffff;
3383bd88451SPaolo Bonzini     }
3393bd88451SPaolo Bonzini }
3403bd88451SPaolo Bonzini 
3413bd88451SPaolo Bonzini static void omap_gp_timer_write(void *opaque, hwaddr addr,
3423bd88451SPaolo Bonzini                 uint32_t value)
3433bd88451SPaolo Bonzini {
3443bd88451SPaolo Bonzini     struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque;
3453bd88451SPaolo Bonzini 
3463bd88451SPaolo Bonzini     switch (addr) {
3473bd88451SPaolo Bonzini     case 0x00:	/* TIDR */
3483bd88451SPaolo Bonzini     case 0x14:	/* TISTAT */
3493bd88451SPaolo Bonzini     case 0x34:	/* TWPS */
3503bd88451SPaolo Bonzini     case 0x3c:	/* TCAR1 */
3513bd88451SPaolo Bonzini     case 0x44:	/* TCAR2 */
3523bd88451SPaolo Bonzini         OMAP_RO_REG(addr);
3533bd88451SPaolo Bonzini         break;
3543bd88451SPaolo Bonzini 
3553bd88451SPaolo Bonzini     case 0x10:	/* TIOCP_CFG */
3563bd88451SPaolo Bonzini         s->config = value & 0x33d;
3573bd88451SPaolo Bonzini         if (((value >> 3) & 3) == 3)				/* IDLEMODE */
3583bd88451SPaolo Bonzini             fprintf(stderr, "%s: illegal IDLEMODE value in TIOCP_CFG\n",
3593bd88451SPaolo Bonzini                             __FUNCTION__);
3603bd88451SPaolo Bonzini         if (value & 2)						/* SOFTRESET */
3613bd88451SPaolo Bonzini             omap_gp_timer_reset(s);
3623bd88451SPaolo Bonzini         break;
3633bd88451SPaolo Bonzini 
3643bd88451SPaolo Bonzini     case 0x18:	/* TISR */
3653bd88451SPaolo Bonzini         if (value & GPT_TCAR_IT)
3663bd88451SPaolo Bonzini             s->capt_num = 0;
3673bd88451SPaolo Bonzini         if (s->status && !(s->status &= ~value))
3683bd88451SPaolo Bonzini             qemu_irq_lower(s->irq);
3693bd88451SPaolo Bonzini         break;
3703bd88451SPaolo Bonzini 
3713bd88451SPaolo Bonzini     case 0x1c:	/* TIER */
3723bd88451SPaolo Bonzini         s->it_ena = value & 7;
3733bd88451SPaolo Bonzini         break;
3743bd88451SPaolo Bonzini 
3753bd88451SPaolo Bonzini     case 0x20:	/* TWER */
3763bd88451SPaolo Bonzini         s->wu_ena = value & 7;
3773bd88451SPaolo Bonzini         break;
3783bd88451SPaolo Bonzini 
3793bd88451SPaolo Bonzini     case 0x24:	/* TCLR */
3803bd88451SPaolo Bonzini         omap_gp_timer_sync(s);
3813bd88451SPaolo Bonzini         s->inout = (value >> 14) & 1;
3823bd88451SPaolo Bonzini         s->capt2 = (value >> 13) & 1;
3833bd88451SPaolo Bonzini         s->pt = (value >> 12) & 1;
3843bd88451SPaolo Bonzini         s->trigger = (value >> 10) & 3;
3853bd88451SPaolo Bonzini         if (s->capture == gpt_capture_none &&
3863bd88451SPaolo Bonzini                         ((value >> 8) & 3) != gpt_capture_none)
3873bd88451SPaolo Bonzini             s->capt_num = 0;
3883bd88451SPaolo Bonzini         s->capture = (value >> 8) & 3;
3893bd88451SPaolo Bonzini         s->scpwm = (value >> 7) & 1;
3903bd88451SPaolo Bonzini         s->ce = (value >> 6) & 1;
3913bd88451SPaolo Bonzini         s->pre = (value >> 5) & 1;
3923bd88451SPaolo Bonzini         s->ptv = (value >> 2) & 7;
3933bd88451SPaolo Bonzini         s->ar = (value >> 1) & 1;
3943bd88451SPaolo Bonzini         s->st = (value >> 0) & 1;
3953bd88451SPaolo Bonzini         if (s->inout && s->trigger != gpt_trigger_none)
3963bd88451SPaolo Bonzini             fprintf(stderr, "%s: GP timer pin must be an output "
3973bd88451SPaolo Bonzini                             "for this trigger mode\n", __FUNCTION__);
3983bd88451SPaolo Bonzini         if (!s->inout && s->capture != gpt_capture_none)
3993bd88451SPaolo Bonzini             fprintf(stderr, "%s: GP timer pin must be an input "
4003bd88451SPaolo Bonzini                             "for this capture mode\n", __FUNCTION__);
4013bd88451SPaolo Bonzini         if (s->trigger == gpt_trigger_none)
4023bd88451SPaolo Bonzini             omap_gp_timer_out(s, s->scpwm);
4033bd88451SPaolo Bonzini         /* TODO: make sure this doesn't overflow 32-bits */
4043bd88451SPaolo Bonzini         s->ticks_per_sec = get_ticks_per_sec() << (s->pre ? s->ptv + 1 : 0);
4053bd88451SPaolo Bonzini         omap_gp_timer_update(s);
4063bd88451SPaolo Bonzini         break;
4073bd88451SPaolo Bonzini 
4083bd88451SPaolo Bonzini     case 0x28:	/* TCRR */
409bc72ad67SAlex Bligh         s->time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
4103bd88451SPaolo Bonzini         s->val = value;
4113bd88451SPaolo Bonzini         omap_gp_timer_update(s);
4123bd88451SPaolo Bonzini         break;
4133bd88451SPaolo Bonzini 
4143bd88451SPaolo Bonzini     case 0x2c:	/* TLDR */
4153bd88451SPaolo Bonzini         s->load_val = value;
4163bd88451SPaolo Bonzini         break;
4173bd88451SPaolo Bonzini 
4183bd88451SPaolo Bonzini     case 0x30:	/* TTGR */
419bc72ad67SAlex Bligh         s->time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
4203bd88451SPaolo Bonzini         s->val = s->load_val;
4213bd88451SPaolo Bonzini         omap_gp_timer_update(s);
4223bd88451SPaolo Bonzini         break;
4233bd88451SPaolo Bonzini 
4243bd88451SPaolo Bonzini     case 0x38:	/* TMAR */
4253bd88451SPaolo Bonzini         omap_gp_timer_sync(s);
4263bd88451SPaolo Bonzini         s->match_val = value;
4273bd88451SPaolo Bonzini         omap_gp_timer_update(s);
4283bd88451SPaolo Bonzini         break;
4293bd88451SPaolo Bonzini 
4303bd88451SPaolo Bonzini     case 0x40:	/* TSICR */
4313bd88451SPaolo Bonzini         s->posted = (value >> 2) & 1;
4323bd88451SPaolo Bonzini         if (value & 2)	/* How much exactly are we supposed to reset? */
4333bd88451SPaolo Bonzini             omap_gp_timer_reset(s);
4343bd88451SPaolo Bonzini         break;
4353bd88451SPaolo Bonzini 
4363bd88451SPaolo Bonzini     default:
4373bd88451SPaolo Bonzini         OMAP_BAD_REG(addr);
4383bd88451SPaolo Bonzini     }
4393bd88451SPaolo Bonzini }
4403bd88451SPaolo Bonzini 
4413bd88451SPaolo Bonzini static void omap_gp_timer_writeh(void *opaque, hwaddr addr,
4423bd88451SPaolo Bonzini                 uint32_t value)
4433bd88451SPaolo Bonzini {
4443bd88451SPaolo Bonzini     struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque;
4453bd88451SPaolo Bonzini 
4463bd88451SPaolo Bonzini     if (addr & 2)
447*77a8257eSStefan Weil         omap_gp_timer_write(opaque, addr, (value << 16) | s->writeh);
4483bd88451SPaolo Bonzini     else
4493bd88451SPaolo Bonzini         s->writeh = (uint16_t) value;
4503bd88451SPaolo Bonzini }
4513bd88451SPaolo Bonzini 
4523bd88451SPaolo Bonzini static const MemoryRegionOps omap_gp_timer_ops = {
4533bd88451SPaolo Bonzini     .old_mmio = {
4543bd88451SPaolo Bonzini         .read = {
4553bd88451SPaolo Bonzini             omap_badwidth_read32,
4563bd88451SPaolo Bonzini             omap_gp_timer_readh,
4573bd88451SPaolo Bonzini             omap_gp_timer_readw,
4583bd88451SPaolo Bonzini         },
4593bd88451SPaolo Bonzini         .write = {
4603bd88451SPaolo Bonzini             omap_badwidth_write32,
4613bd88451SPaolo Bonzini             omap_gp_timer_writeh,
4623bd88451SPaolo Bonzini             omap_gp_timer_write,
4633bd88451SPaolo Bonzini         },
4643bd88451SPaolo Bonzini     },
4653bd88451SPaolo Bonzini     .endianness = DEVICE_NATIVE_ENDIAN,
4663bd88451SPaolo Bonzini };
4673bd88451SPaolo Bonzini 
4683bd88451SPaolo Bonzini struct omap_gp_timer_s *omap_gp_timer_init(struct omap_target_agent_s *ta,
4693bd88451SPaolo Bonzini                 qemu_irq irq, omap_clk fclk, omap_clk iclk)
4703bd88451SPaolo Bonzini {
4713bd88451SPaolo Bonzini     struct omap_gp_timer_s *s = (struct omap_gp_timer_s *)
4723bd88451SPaolo Bonzini             g_malloc0(sizeof(struct omap_gp_timer_s));
4733bd88451SPaolo Bonzini 
4743bd88451SPaolo Bonzini     s->ta = ta;
4753bd88451SPaolo Bonzini     s->irq = irq;
4763bd88451SPaolo Bonzini     s->clk = fclk;
477bc72ad67SAlex Bligh     s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, omap_gp_timer_tick, s);
478bc72ad67SAlex Bligh     s->match = timer_new_ns(QEMU_CLOCK_VIRTUAL, omap_gp_timer_match, s);
479f3c7d038SAndreas Färber     s->in = qemu_allocate_irq(omap_gp_timer_input, s, 0);
4803bd88451SPaolo Bonzini     omap_gp_timer_reset(s);
4813bd88451SPaolo Bonzini     omap_gp_timer_clk_setup(s);
4823bd88451SPaolo Bonzini 
4832c9b15caSPaolo Bonzini     memory_region_init_io(&s->iomem, NULL, &omap_gp_timer_ops, s, "omap.gptimer",
4843bd88451SPaolo Bonzini                           omap_l4_region_size(ta, 0));
4853bd88451SPaolo Bonzini     omap_l4_attach(ta, 0, &s->iomem);
4863bd88451SPaolo Bonzini 
4873bd88451SPaolo Bonzini     return s;
4883bd88451SPaolo Bonzini }
489