From patchwork Fri May 28 17:43:16 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: cmchao X-Patchwork-Id: 53939 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) by ozlabs.org (Postfix) with ESMTP id 034FEB7D12 for ; Sat, 29 May 2010 04:04:46 +1000 (EST) Received: from localhost ([127.0.0.1]:43359 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1OI3qF-0004mn-Jc for incoming@patchwork.ozlabs.org; Fri, 28 May 2010 13:59:31 -0400 Received: from [140.186.70.92] (port=39645 helo=eggs.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1OI3b6-0005Wv-Mx for qemu-devel@nongnu.org; Fri, 28 May 2010 13:43:55 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.69) (envelope-from ) id 1OI3b4-0007wj-3u for qemu-devel@nongnu.org; Fri, 28 May 2010 13:43:52 -0400 Received: from mail-pz0-f204.google.com ([209.85.222.204]:43219) by eggs.gnu.org with esmtp (Exim 4.69) (envelope-from ) id 1OI3b3-0007wO-Li for qemu-devel@nongnu.org; Fri, 28 May 2010 13:43:50 -0400 Received: by pzk42 with SMTP id 42so700628pzk.4 for ; Fri, 28 May 2010 10:43:48 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=domainkey-signature:received:received:from:to:cc:subject:date :message-id:x-mailer:in-reply-to:references; bh=4R+HoASgQZ6aiDDkS8T2lTJu17apiSwe1t5tOoTEHaM=; b=F2zQfc75p+KwW8kFLAAhQAdDONFD4gn3Xp4a9hry1rRhWcO7w+MeC03Oy2rAOSOYO5 PIA7o4uHemmzytHVU8W8Pm0Pwvfkh/Pj96OCRT8Z4QPNzN6oWsJ2W4Zbj7syFXUJ5UfH 6KKCzKi3l+8Oh2gemKqOmFb/8xkO20Im/Jktw= DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=from:to:cc:subject:date:message-id:x-mailer:in-reply-to:references; b=yBLF1VnIqNwBmkhvMVjgVLDtO8mTC9mnwhbsCAUvT6qjoDuBIlju4/hVuHnGDV4Qld cO9Rek0+o/XQgQrP4lPtD/751+oijFx4tP00QB66DYXwMLlFveDLF4V0GyORNvEC2Cwv wMOr80QMq/aBs5RfLVLOfsyPHyleSC5mvhXKY= Received: by 10.141.213.24 with SMTP id p24mr478841rvq.291.1275068628002; Fri, 28 May 2010 10:43:48 -0700 (PDT) Received: from localhost.localdomain (114-44-100-131.dynamic.hinet.net [114.44.100.131]) by mx.google.com with ESMTPS id o38sm2248891rvp.12.2010.05.28.10.43.45 (version=TLSv1/SSLv3 cipher=RC4-MD5); Fri, 28 May 2010 10:43:47 -0700 (PDT) From: cmchao To: qemu-devel@nongnu.org Date: Sat, 29 May 2010 01:43:16 +0800 Message-Id: <1275068605-14475-4-git-send-email-cmchao@gmail.com> X-Mailer: git-send-email 1.7.0.4 In-Reply-To: <1275068605-14475-1-git-send-email-cmchao@gmail.com> References: <1275068605-14475-1-git-send-email-cmchao@gmail.com> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.6 (newer, 2) Cc: cmchao Subject: [Qemu-devel] [PATCH 03/12] hw/omap2.c : separate gptimer module X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Signed-off-by: cmchao --- Makefile.target | 2 +- hw/omap.h | 2 + hw/omap2.c | 462 -------------------------------------------------- hw/omap_gptimer.c | 484 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 487 insertions(+), 463 deletions(-) create mode 100644 hw/omap_gptimer.c diff --git a/Makefile.target b/Makefile.target index eb5c84a..c29dee0 100644 --- a/Makefile.target +++ b/Makefile.target @@ -264,7 +264,7 @@ obj-arm-y += pxa2xx_lcd.o pxa2xx_mmci.o pxa2xx_pcmcia.o pxa2xx_keypad.o obj-arm-y += gumstix.o obj-arm-y += zaurus.o ide/microdrive.o spitz.o tosa.o tc6393xb.o obj-arm-y += omap1.o omap_lcdc.o omap_dma.o omap_clk.o omap_mmc.o omap_i2c.o omap_gpio.o -obj-arm-y += omap2.o omap_dss.o soc_dma.o +obj-arm-y += omap2.o omap_dss.o soc_dma.o omap_gptimer.o obj-arm-y += omap_sx1.o palm.o tsc210x.o obj-arm-y += nseries.o blizzard.o onenand.o vga.o cbus.o tusb6010.o usb-musb.o obj-arm-y += mst_fpga.o mainstone.o diff --git a/hw/omap.h b/hw/omap.h index a37ee54..14fdb6f 100644 --- a/hw/omap.h +++ b/hw/omap.h @@ -631,9 +631,11 @@ struct omap_mpu_timer_s; struct omap_mpu_timer_s *omap_mpu_timer_init(target_phys_addr_t base, qemu_irq irq, omap_clk clk); +/* OMAP2 gp timer */ struct omap_gp_timer_s; struct omap_gp_timer_s *omap_gp_timer_init(struct omap_target_agent_s *ta, qemu_irq irq, omap_clk fclk, omap_clk iclk); +void omap_gp_timer_reset(struct omap_gp_timer_s *s); struct omap_watchdog_timer_s; struct omap_watchdog_timer_s *omap_wd_timer_init(target_phys_addr_t base, diff --git a/hw/omap2.c b/hw/omap2.c index bf5539d..890397b 100644 --- a/hw/omap2.c +++ b/hw/omap2.c @@ -27,468 +27,6 @@ #include "soc_dma.h" #include "audio/audio.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(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(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(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); -} - -static 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(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(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(vm_clock, omap_gp_timer_tick, s); - s->match = qemu_new_timer(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; -} - /* 32-kHz Sync Timer of the OMAP2 */ static uint32_t omap_synctimer_read(struct omap_synctimer_s *s) { return muldiv64(qemu_get_clock(vm_clock), 0x8000, get_ticks_per_sec()); diff --git a/hw/omap_gptimer.c b/hw/omap_gptimer.c new file mode 100644 index 0000000..9c0f9f2 --- /dev/null +++ b/hw/omap_gptimer.c @@ -0,0 +1,484 @@ +/* + * TI OMAP2 general purpose timers emulation. + * + * Copyright (C) 2007-2008 Nokia Corporation + * Written by Andrzej Zaborowski + * + * 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 . + */ +#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(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(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(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(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(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(vm_clock, omap_gp_timer_tick, s); + s->match = qemu_new_timer(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; +}