mirror of
				https://github.com/qemu/qemu.git
				synced 2025-10-27 05:04:40 +00:00 
			
		
		
		
	 7447545544
			
		
	
	
		7447545544
		
	
	
	
	
		
			
			This was done with:
    sed -i 's/qemu_get_clock\>/qemu_get_clock_ns/' \
        $(git grep -l 'qemu_get_clock\>' )
    sed -i 's/qemu_new_timer\>/qemu_new_timer_ns/' \
        $(git grep -l 'qemu_new_timer\>' )
after checking that get_clock and new_timer never occur twice
on the same line.  There were no missed occurrences; however, even
if there had been, they would have been caught by the compiler.
There was exactly one false positive in qemu_run_timers:
     -    current_time = qemu_get_clock (clock);
     +    current_time = qemu_get_clock_ns (clock);
which is of course not in this patch.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
		
	
			
		
			
				
	
	
		
			485 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			485 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * 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 {
 | |
|     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) {
 | |
|         distance = qemu_get_clock_ns(vm_clock) - timer->time;
 | |
|         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);
 | |
|         timer->time = qemu_get_clock_ns(vm_clock);
 | |
|     }
 | |
| }
 | |
| 
 | |
| 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;
 | |
|         timer->time = qemu_get_clock_ns(vm_clock);
 | |
|     }
 | |
| 
 | |
|     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 CPUReadMemoryFunc * const omap_gp_timer_readfn[] = {
 | |
|     omap_badwidth_read32,
 | |
|     omap_gp_timer_readh,
 | |
|     omap_gp_timer_readw,
 | |
| };
 | |
| 
 | |
| 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 */
 | |
|         s->time = qemu_get_clock_ns(vm_clock);
 | |
|         s->val = value;
 | |
|         omap_gp_timer_update(s);
 | |
|         break;
 | |
| 
 | |
|     case 0x2c:	/* TLDR */
 | |
|         s->load_val = value;
 | |
|         break;
 | |
| 
 | |
|     case 0x30:	/* TTGR */
 | |
|         s->time = qemu_get_clock_ns(vm_clock);
 | |
|         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;
 | |
| }
 | |
| 
 | |
| static CPUWriteMemoryFunc * const omap_gp_timer_writefn[] = {
 | |
|     omap_badwidth_write32,
 | |
|     omap_gp_timer_writeh,
 | |
|     omap_gp_timer_write,
 | |
| };
 | |
| 
 | |
| struct omap_gp_timer_s *omap_gp_timer_init(struct omap_target_agent_s *ta,
 | |
|                 qemu_irq irq, omap_clk fclk, omap_clk iclk)
 | |
| {
 | |
|     int iomemtype;
 | |
|     struct omap_gp_timer_s *s = (struct omap_gp_timer_s *)
 | |
|             qemu_mallocz(sizeof(struct omap_gp_timer_s));
 | |
| 
 | |
|     s->ta = ta;
 | |
|     s->irq = irq;
 | |
|     s->clk = fclk;
 | |
|     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);
 | |
|     s->in = qemu_allocate_irqs(omap_gp_timer_input, s, 1)[0];
 | |
|     omap_gp_timer_reset(s);
 | |
|     omap_gp_timer_clk_setup(s);
 | |
| 
 | |
|     iomemtype = l4_register_io_memory(omap_gp_timer_readfn,
 | |
|                     omap_gp_timer_writefn, s);
 | |
|     omap_l4_attach(ta, 0, iomemtype);
 | |
| 
 | |
|     return s;
 | |
| }
 |