| 
									
										
										
										
											2010-05-31 23:54:15 +08:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * TI OMAP2 general purpose timers emulation. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright (C) 2007-2008 Nokia Corporation | 
					
						
							|  |  |  |  * Written by Andrzej Zaborowski <andrew@openedhand.com> | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is free software; you can redistribute it and/or | 
					
						
							|  |  |  |  * modify it under the terms of the GNU General Public License as | 
					
						
							|  |  |  |  * published by the Free Software Foundation; either version 2 or | 
					
						
							|  |  |  |  * (at your option) any later version of the License. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is distributed in the hope that it will be useful, | 
					
						
							|  |  |  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
					
						
							|  |  |  |  * GNU General Public License for more details. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * You should have received a copy of the GNU General Public License along | 
					
						
							|  |  |  |  * with this program; if not, see <http://www.gnu.org/licenses/>.
 | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | #include "hw.h"
 | 
					
						
							|  |  |  | #include "qemu-timer.h"
 | 
					
						
							|  |  |  | #include "omap.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* GP timers */ | 
					
						
							|  |  |  | struct omap_gp_timer_s { | 
					
						
							| 
									
										
										
										
											2011-11-24 14:32:34 +02:00
										 |  |  |     MemoryRegion iomem; | 
					
						
							| 
									
										
										
										
											2010-05-31 23:54:15 +08:00
										 |  |  |     qemu_irq irq; | 
					
						
							|  |  |  |     qemu_irq wkup; | 
					
						
							|  |  |  |     qemu_irq in; | 
					
						
							|  |  |  |     qemu_irq out; | 
					
						
							|  |  |  |     omap_clk clk; | 
					
						
							|  |  |  |     QEMUTimer *timer; | 
					
						
							|  |  |  |     QEMUTimer *match; | 
					
						
							|  |  |  |     struct omap_target_agent_s *ta; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     int in_val; | 
					
						
							|  |  |  |     int out_val; | 
					
						
							|  |  |  |     int64_t time; | 
					
						
							|  |  |  |     int64_t rate; | 
					
						
							|  |  |  |     int64_t ticks_per_sec; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     int16_t config; | 
					
						
							|  |  |  |     int status; | 
					
						
							|  |  |  |     int it_ena; | 
					
						
							|  |  |  |     int wu_ena; | 
					
						
							|  |  |  |     int enable; | 
					
						
							|  |  |  |     int inout; | 
					
						
							|  |  |  |     int capt2; | 
					
						
							|  |  |  |     int pt; | 
					
						
							|  |  |  |     enum { | 
					
						
							|  |  |  |         gpt_trigger_none, gpt_trigger_overflow, gpt_trigger_both | 
					
						
							|  |  |  |     } trigger; | 
					
						
							|  |  |  |     enum { | 
					
						
							|  |  |  |         gpt_capture_none, gpt_capture_rising, | 
					
						
							|  |  |  |         gpt_capture_falling, gpt_capture_both | 
					
						
							|  |  |  |     } capture; | 
					
						
							|  |  |  |     int scpwm; | 
					
						
							|  |  |  |     int ce; | 
					
						
							|  |  |  |     int pre; | 
					
						
							|  |  |  |     int ptv; | 
					
						
							|  |  |  |     int ar; | 
					
						
							|  |  |  |     int st; | 
					
						
							|  |  |  |     int posted; | 
					
						
							|  |  |  |     uint32_t val; | 
					
						
							|  |  |  |     uint32_t load_val; | 
					
						
							|  |  |  |     uint32_t capture_val[2]; | 
					
						
							|  |  |  |     uint32_t match_val; | 
					
						
							|  |  |  |     int capt_num; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     uint16_t writeh;	/* LSB */ | 
					
						
							|  |  |  |     uint16_t readh;	/* MSB */ | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define GPT_TCAR_IT	(1 << 2)
 | 
					
						
							|  |  |  | #define GPT_OVF_IT	(1 << 1)
 | 
					
						
							|  |  |  | #define GPT_MAT_IT	(1 << 0)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline void omap_gp_timer_intr(struct omap_gp_timer_s *timer, int it) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (timer->it_ena & it) { | 
					
						
							|  |  |  |         if (!timer->status) | 
					
						
							|  |  |  |             qemu_irq_raise(timer->irq); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         timer->status |= it; | 
					
						
							|  |  |  |         /* Or are the status bits set even when masked?
 | 
					
						
							|  |  |  |          * i.e. is masking applied before or after the status register?  */ | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (timer->wu_ena & it) | 
					
						
							|  |  |  |         qemu_irq_pulse(timer->wkup); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline void omap_gp_timer_out(struct omap_gp_timer_s *timer, int level) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (!timer->inout && timer->out_val != level) { | 
					
						
							|  |  |  |         timer->out_val = level; | 
					
						
							|  |  |  |         qemu_set_irq(timer->out, level); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline uint32_t omap_gp_timer_read(struct omap_gp_timer_s *timer) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     uint64_t distance; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (timer->st && timer->rate) { | 
					
						
							| 
									
										
										
										
											2011-03-11 16:47:48 +01:00
										 |  |  |         distance = qemu_get_clock_ns(vm_clock) - timer->time; | 
					
						
							| 
									
										
										
										
											2010-05-31 23:54:15 +08:00
										 |  |  |         distance = muldiv64(distance, timer->rate, timer->ticks_per_sec); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (distance >= 0xffffffff - timer->val) | 
					
						
							|  |  |  |             return 0xffffffff; | 
					
						
							|  |  |  |         else | 
					
						
							|  |  |  |             return timer->val + distance; | 
					
						
							|  |  |  |     } else | 
					
						
							|  |  |  |         return timer->val; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline void omap_gp_timer_sync(struct omap_gp_timer_s *timer) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (timer->st) { | 
					
						
							|  |  |  |         timer->val = omap_gp_timer_read(timer); | 
					
						
							| 
									
										
										
										
											2011-03-11 16:47:48 +01:00
										 |  |  |         timer->time = qemu_get_clock_ns(vm_clock); | 
					
						
							| 
									
										
										
										
											2010-05-31 23:54:15 +08:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline void omap_gp_timer_update(struct omap_gp_timer_s *timer) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int64_t expires, matches; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (timer->st && timer->rate) { | 
					
						
							|  |  |  |         expires = muldiv64(0x100000000ll - timer->val, | 
					
						
							|  |  |  |                         timer->ticks_per_sec, timer->rate); | 
					
						
							|  |  |  |         qemu_mod_timer(timer->timer, timer->time + expires); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (timer->ce && timer->match_val >= timer->val) { | 
					
						
							|  |  |  |             matches = muldiv64(timer->match_val - timer->val, | 
					
						
							|  |  |  |                             timer->ticks_per_sec, timer->rate); | 
					
						
							|  |  |  |             qemu_mod_timer(timer->match, timer->time + matches); | 
					
						
							|  |  |  |         } else | 
					
						
							|  |  |  |             qemu_del_timer(timer->match); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         qemu_del_timer(timer->timer); | 
					
						
							|  |  |  |         qemu_del_timer(timer->match); | 
					
						
							|  |  |  |         omap_gp_timer_out(timer, timer->scpwm); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline void omap_gp_timer_trigger(struct omap_gp_timer_s *timer) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (timer->pt) | 
					
						
							|  |  |  |         /* TODO in overflow-and-match mode if the first event to
 | 
					
						
							|  |  |  |          * occur is the match, don't toggle.  */ | 
					
						
							|  |  |  |         omap_gp_timer_out(timer, !timer->out_val); | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |         /* TODO inverted pulse on timer->out_val == 1?  */ | 
					
						
							|  |  |  |         qemu_irq_pulse(timer->out); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void omap_gp_timer_tick(void *opaque) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     struct omap_gp_timer_s *timer = (struct omap_gp_timer_s *) opaque; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!timer->ar) { | 
					
						
							|  |  |  |         timer->st = 0; | 
					
						
							|  |  |  |         timer->val = 0; | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         timer->val = timer->load_val; | 
					
						
							| 
									
										
										
										
											2011-03-11 16:47:48 +01:00
										 |  |  |         timer->time = qemu_get_clock_ns(vm_clock); | 
					
						
							| 
									
										
										
										
											2010-05-31 23:54:15 +08:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (timer->trigger == gpt_trigger_overflow || | 
					
						
							|  |  |  |                     timer->trigger == gpt_trigger_both) | 
					
						
							|  |  |  |         omap_gp_timer_trigger(timer); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     omap_gp_timer_intr(timer, GPT_OVF_IT); | 
					
						
							|  |  |  |     omap_gp_timer_update(timer); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void omap_gp_timer_match(void *opaque) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     struct omap_gp_timer_s *timer = (struct omap_gp_timer_s *) opaque; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (timer->trigger == gpt_trigger_both) | 
					
						
							|  |  |  |         omap_gp_timer_trigger(timer); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     omap_gp_timer_intr(timer, GPT_MAT_IT); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void omap_gp_timer_input(void *opaque, int line, int on) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque; | 
					
						
							|  |  |  |     int trigger; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     switch (s->capture) { | 
					
						
							|  |  |  |     default: | 
					
						
							|  |  |  |     case gpt_capture_none: | 
					
						
							|  |  |  |         trigger = 0; | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case gpt_capture_rising: | 
					
						
							|  |  |  |         trigger = !s->in_val && on; | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case gpt_capture_falling: | 
					
						
							|  |  |  |         trigger = s->in_val && !on; | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case gpt_capture_both: | 
					
						
							|  |  |  |         trigger = (s->in_val == !on); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     s->in_val = on; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (s->inout && trigger && s->capt_num < 2) { | 
					
						
							|  |  |  |         s->capture_val[s->capt_num] = omap_gp_timer_read(s); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (s->capt2 == s->capt_num ++) | 
					
						
							|  |  |  |             omap_gp_timer_intr(s, GPT_TCAR_IT); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void omap_gp_timer_clk_update(void *opaque, int line, int on) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     struct omap_gp_timer_s *timer = (struct omap_gp_timer_s *) opaque; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     omap_gp_timer_sync(timer); | 
					
						
							|  |  |  |     timer->rate = on ? omap_clk_getrate(timer->clk) : 0; | 
					
						
							|  |  |  |     omap_gp_timer_update(timer); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void omap_gp_timer_clk_setup(struct omap_gp_timer_s *timer) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     omap_clk_adduser(timer->clk, | 
					
						
							|  |  |  |                     qemu_allocate_irqs(omap_gp_timer_clk_update, timer, 1)[0]); | 
					
						
							|  |  |  |     timer->rate = omap_clk_getrate(timer->clk); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void omap_gp_timer_reset(struct omap_gp_timer_s *s) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     s->config = 0x000; | 
					
						
							|  |  |  |     s->status = 0; | 
					
						
							|  |  |  |     s->it_ena = 0; | 
					
						
							|  |  |  |     s->wu_ena = 0; | 
					
						
							|  |  |  |     s->inout = 0; | 
					
						
							|  |  |  |     s->capt2 = 0; | 
					
						
							|  |  |  |     s->capt_num = 0; | 
					
						
							|  |  |  |     s->pt = 0; | 
					
						
							|  |  |  |     s->trigger = gpt_trigger_none; | 
					
						
							|  |  |  |     s->capture = gpt_capture_none; | 
					
						
							|  |  |  |     s->scpwm = 0; | 
					
						
							|  |  |  |     s->ce = 0; | 
					
						
							|  |  |  |     s->pre = 0; | 
					
						
							|  |  |  |     s->ptv = 0; | 
					
						
							|  |  |  |     s->ar = 0; | 
					
						
							|  |  |  |     s->st = 0; | 
					
						
							|  |  |  |     s->posted = 1; | 
					
						
							|  |  |  |     s->val = 0x00000000; | 
					
						
							|  |  |  |     s->load_val = 0x00000000; | 
					
						
							|  |  |  |     s->capture_val[0] = 0x00000000; | 
					
						
							|  |  |  |     s->capture_val[1] = 0x00000000; | 
					
						
							|  |  |  |     s->match_val = 0x00000000; | 
					
						
							|  |  |  |     omap_gp_timer_update(s); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static uint32_t omap_gp_timer_readw(void *opaque, target_phys_addr_t addr) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     switch (addr) { | 
					
						
							|  |  |  |     case 0x00:	/* TIDR */ | 
					
						
							|  |  |  |         return 0x21; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case 0x10:	/* TIOCP_CFG */ | 
					
						
							|  |  |  |         return s->config; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case 0x14:	/* TISTAT */ | 
					
						
							|  |  |  |         /* ??? When's this bit reset? */ | 
					
						
							|  |  |  |         return 1;						/* RESETDONE */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case 0x18:	/* TISR */ | 
					
						
							|  |  |  |         return s->status; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case 0x1c:	/* TIER */ | 
					
						
							|  |  |  |         return s->it_ena; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case 0x20:	/* TWER */ | 
					
						
							|  |  |  |         return s->wu_ena; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case 0x24:	/* TCLR */ | 
					
						
							|  |  |  |         return (s->inout << 14) | | 
					
						
							|  |  |  |                 (s->capt2 << 13) | | 
					
						
							|  |  |  |                 (s->pt << 12) | | 
					
						
							|  |  |  |                 (s->trigger << 10) | | 
					
						
							|  |  |  |                 (s->capture << 8) | | 
					
						
							|  |  |  |                 (s->scpwm << 7) | | 
					
						
							|  |  |  |                 (s->ce << 6) | | 
					
						
							|  |  |  |                 (s->pre << 5) | | 
					
						
							|  |  |  |                 (s->ptv << 2) | | 
					
						
							|  |  |  |                 (s->ar << 1) | | 
					
						
							|  |  |  |                 (s->st << 0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case 0x28:	/* TCRR */ | 
					
						
							|  |  |  |         return omap_gp_timer_read(s); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case 0x2c:	/* TLDR */ | 
					
						
							|  |  |  |         return s->load_val; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case 0x30:	/* TTGR */ | 
					
						
							|  |  |  |         return 0xffffffff; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case 0x34:	/* TWPS */ | 
					
						
							|  |  |  |         return 0x00000000;	/* No posted writes pending.  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case 0x38:	/* TMAR */ | 
					
						
							|  |  |  |         return s->match_val; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case 0x3c:	/* TCAR1 */ | 
					
						
							|  |  |  |         return s->capture_val[0]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case 0x40:	/* TSICR */ | 
					
						
							|  |  |  |         return s->posted << 2; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case 0x44:	/* TCAR2 */ | 
					
						
							|  |  |  |         return s->capture_val[1]; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     OMAP_BAD_REG(addr); | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static uint32_t omap_gp_timer_readh(void *opaque, target_phys_addr_t addr) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque; | 
					
						
							|  |  |  |     uint32_t ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (addr & 2) | 
					
						
							|  |  |  |         return s->readh; | 
					
						
							|  |  |  |     else { | 
					
						
							|  |  |  |         ret = omap_gp_timer_readw(opaque, addr); | 
					
						
							|  |  |  |         s->readh = ret >> 16; | 
					
						
							|  |  |  |         return ret & 0xffff; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void omap_gp_timer_write(void *opaque, target_phys_addr_t addr, | 
					
						
							|  |  |  |                 uint32_t value) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     switch (addr) { | 
					
						
							|  |  |  |     case 0x00:	/* TIDR */ | 
					
						
							|  |  |  |     case 0x14:	/* TISTAT */ | 
					
						
							|  |  |  |     case 0x34:	/* TWPS */ | 
					
						
							|  |  |  |     case 0x3c:	/* TCAR1 */ | 
					
						
							|  |  |  |     case 0x44:	/* TCAR2 */ | 
					
						
							|  |  |  |         OMAP_RO_REG(addr); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case 0x10:	/* TIOCP_CFG */ | 
					
						
							|  |  |  |         s->config = value & 0x33d; | 
					
						
							|  |  |  |         if (((value >> 3) & 3) == 3)				/* IDLEMODE */ | 
					
						
							|  |  |  |             fprintf(stderr, "%s: illegal IDLEMODE value in TIOCP_CFG\n", | 
					
						
							|  |  |  |                             __FUNCTION__); | 
					
						
							|  |  |  |         if (value & 2)						/* SOFTRESET */ | 
					
						
							|  |  |  |             omap_gp_timer_reset(s); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case 0x18:	/* TISR */ | 
					
						
							|  |  |  |         if (value & GPT_TCAR_IT) | 
					
						
							|  |  |  |             s->capt_num = 0; | 
					
						
							|  |  |  |         if (s->status && !(s->status &= ~value)) | 
					
						
							|  |  |  |             qemu_irq_lower(s->irq); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case 0x1c:	/* TIER */ | 
					
						
							|  |  |  |         s->it_ena = value & 7; | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case 0x20:	/* TWER */ | 
					
						
							|  |  |  |         s->wu_ena = value & 7; | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case 0x24:	/* TCLR */ | 
					
						
							|  |  |  |         omap_gp_timer_sync(s); | 
					
						
							|  |  |  |         s->inout = (value >> 14) & 1; | 
					
						
							|  |  |  |         s->capt2 = (value >> 13) & 1; | 
					
						
							|  |  |  |         s->pt = (value >> 12) & 1; | 
					
						
							|  |  |  |         s->trigger = (value >> 10) & 3; | 
					
						
							|  |  |  |         if (s->capture == gpt_capture_none && | 
					
						
							|  |  |  |                         ((value >> 8) & 3) != gpt_capture_none) | 
					
						
							|  |  |  |             s->capt_num = 0; | 
					
						
							|  |  |  |         s->capture = (value >> 8) & 3; | 
					
						
							|  |  |  |         s->scpwm = (value >> 7) & 1; | 
					
						
							|  |  |  |         s->ce = (value >> 6) & 1; | 
					
						
							|  |  |  |         s->pre = (value >> 5) & 1; | 
					
						
							|  |  |  |         s->ptv = (value >> 2) & 7; | 
					
						
							|  |  |  |         s->ar = (value >> 1) & 1; | 
					
						
							|  |  |  |         s->st = (value >> 0) & 1; | 
					
						
							|  |  |  |         if (s->inout && s->trigger != gpt_trigger_none) | 
					
						
							|  |  |  |             fprintf(stderr, "%s: GP timer pin must be an output " | 
					
						
							|  |  |  |                             "for this trigger mode\n", __FUNCTION__); | 
					
						
							|  |  |  |         if (!s->inout && s->capture != gpt_capture_none) | 
					
						
							|  |  |  |             fprintf(stderr, "%s: GP timer pin must be an input " | 
					
						
							|  |  |  |                             "for this capture mode\n", __FUNCTION__); | 
					
						
							|  |  |  |         if (s->trigger == gpt_trigger_none) | 
					
						
							|  |  |  |             omap_gp_timer_out(s, s->scpwm); | 
					
						
							|  |  |  |         /* TODO: make sure this doesn't overflow 32-bits */ | 
					
						
							|  |  |  |         s->ticks_per_sec = get_ticks_per_sec() << (s->pre ? s->ptv + 1 : 0); | 
					
						
							|  |  |  |         omap_gp_timer_update(s); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case 0x28:	/* TCRR */ | 
					
						
							| 
									
										
										
										
											2011-03-11 16:47:48 +01:00
										 |  |  |         s->time = qemu_get_clock_ns(vm_clock); | 
					
						
							| 
									
										
										
										
											2010-05-31 23:54:15 +08:00
										 |  |  |         s->val = value; | 
					
						
							|  |  |  |         omap_gp_timer_update(s); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case 0x2c:	/* TLDR */ | 
					
						
							|  |  |  |         s->load_val = value; | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case 0x30:	/* TTGR */ | 
					
						
							| 
									
										
										
										
											2011-03-11 16:47:48 +01:00
										 |  |  |         s->time = qemu_get_clock_ns(vm_clock); | 
					
						
							| 
									
										
										
										
											2010-05-31 23:54:15 +08:00
										 |  |  |         s->val = s->load_val; | 
					
						
							|  |  |  |         omap_gp_timer_update(s); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case 0x38:	/* TMAR */ | 
					
						
							|  |  |  |         omap_gp_timer_sync(s); | 
					
						
							|  |  |  |         s->match_val = value; | 
					
						
							|  |  |  |         omap_gp_timer_update(s); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case 0x40:	/* TSICR */ | 
					
						
							|  |  |  |         s->posted = (value >> 2) & 1; | 
					
						
							|  |  |  |         if (value & 2)	/* How much exactly are we supposed to reset? */ | 
					
						
							|  |  |  |             omap_gp_timer_reset(s); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     default: | 
					
						
							|  |  |  |         OMAP_BAD_REG(addr); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void omap_gp_timer_writeh(void *opaque, target_phys_addr_t addr, | 
					
						
							|  |  |  |                 uint32_t value) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (addr & 2) | 
					
						
							|  |  |  |         return omap_gp_timer_write(opaque, addr, (value << 16) | s->writeh); | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |         s->writeh = (uint16_t) value; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-11-24 14:32:34 +02:00
										 |  |  | static const MemoryRegionOps omap_gp_timer_ops = { | 
					
						
							|  |  |  |     .old_mmio = { | 
					
						
							|  |  |  |         .read = { | 
					
						
							|  |  |  |             omap_badwidth_read32, | 
					
						
							|  |  |  |             omap_gp_timer_readh, | 
					
						
							|  |  |  |             omap_gp_timer_readw, | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         .write = { | 
					
						
							|  |  |  |             omap_badwidth_write32, | 
					
						
							|  |  |  |             omap_gp_timer_writeh, | 
					
						
							|  |  |  |             omap_gp_timer_write, | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |     .endianness = DEVICE_NATIVE_ENDIAN, | 
					
						
							| 
									
										
										
										
											2010-05-31 23:54:15 +08:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct omap_gp_timer_s *omap_gp_timer_init(struct omap_target_agent_s *ta, | 
					
						
							|  |  |  |                 qemu_irq irq, omap_clk fclk, omap_clk iclk) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) | 
					
						
							| 
									
										
										
										
											2011-08-20 22:09:37 -05:00
										 |  |  |             g_malloc0(sizeof(struct omap_gp_timer_s)); | 
					
						
							| 
									
										
										
										
											2010-05-31 23:54:15 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     s->ta = ta; | 
					
						
							|  |  |  |     s->irq = irq; | 
					
						
							|  |  |  |     s->clk = fclk; | 
					
						
							| 
									
										
										
										
											2011-03-11 16:47:48 +01:00
										 |  |  |     s->timer = qemu_new_timer_ns(vm_clock, omap_gp_timer_tick, s); | 
					
						
							|  |  |  |     s->match = qemu_new_timer_ns(vm_clock, omap_gp_timer_match, s); | 
					
						
							| 
									
										
										
										
											2010-05-31 23:54:15 +08:00
										 |  |  |     s->in = qemu_allocate_irqs(omap_gp_timer_input, s, 1)[0]; | 
					
						
							|  |  |  |     omap_gp_timer_reset(s); | 
					
						
							|  |  |  |     omap_gp_timer_clk_setup(s); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-11-24 14:32:34 +02:00
										 |  |  |     memory_region_init_io(&s->iomem, &omap_gp_timer_ops, s, "omap.gptimer", | 
					
						
							|  |  |  |                           omap_l4_region_size(ta, 0)); | 
					
						
							| 
									
										
										
										
											2011-11-28 15:40:49 +02:00
										 |  |  |     omap_l4_attach(ta, 0, &s->iomem); | 
					
						
							| 
									
										
										
										
											2010-05-31 23:54:15 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return s; | 
					
						
							|  |  |  | } |