From patchwork Fri Jan 25 08:19:53 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kuo-Jung Su X-Patchwork-Id: 215592 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id AF8C92C0093 for ; Fri, 25 Jan 2013 20:10:09 +1100 (EST) Received: from localhost ([::1]:56997 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TyfIV-0002Pk-Jn for incoming@patchwork.ozlabs.org; Fri, 25 Jan 2013 04:10:07 -0500 Received: from eggs.gnu.org ([208.118.235.92]:44780) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TyeXD-0005oj-5j for qemu-devel@nongnu.org; Fri, 25 Jan 2013 03:21:23 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1TyeX9-0008H4-OX for qemu-devel@nongnu.org; Fri, 25 Jan 2013 03:21:15 -0500 Received: from mail-pa0-f41.google.com ([209.85.220.41]:63847) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TyeX9-0008Gv-Eq for qemu-devel@nongnu.org; Fri, 25 Jan 2013 03:21:11 -0500 Received: by mail-pa0-f41.google.com with SMTP id bj3so125780pad.28 for ; Fri, 25 Jan 2013 00:21:10 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=x-received:from:to:cc:subject:date:message-id:x-mailer:in-reply-to :references:in-reply-to:references; bh=ULYzJZ/pSYy93aUGBrZ6gyPZ5bKmJjceil78kerOhOw=; b=rPz8aYUhPR5VTm5OKUasUEURQBoGgb4qWBYC5NgToNgpef7Y5UE/svkTo6xzlvivEW 3n6t+/83y4xhUMPM+Y3JUezV0ppVdwZQFL+DDSMBx/a7RqbQmIuX9YrqOfvfLF7G6plr +g4jRd7ogswmK43tkPFDY4q9pDHjq7sw8IjljyR/T823KPHCwrYr3Th/6HpMIHAegwE6 v4o1SBq6o/9NJoxV43Q87slxMVDQfmWy8CAaf98gWe0rA9IR7AqiJzmzOBrHJITdulS3 c6SFFDs21pZbvxbkhHo9bteB7MrPclgR2MaDnJsDYqM55YybFtnZCrcMBpPiIqT7BMsd zrmA== X-Received: by 10.68.241.103 with SMTP id wh7mr12249815pbc.153.1359102070754; Fri, 25 Jan 2013 00:21:10 -0800 (PST) Received: from localhost.localdomain ([220.132.37.35]) by mx.google.com with ESMTPS id uh9sm246180pbc.65.2013.01.25.00.21.07 (version=TLSv1 cipher=DES-CBC3-SHA bits=168/168); Fri, 25 Jan 2013 00:21:10 -0800 (PST) From: Kuo-Jung Su To: qemu-devel@nongnu.org Date: Fri, 25 Jan 2013 16:19:53 +0800 Message-Id: <1359101996-11667-18-git-send-email-dantesu@gmail.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1359101996-11667-1-git-send-email-dantesu@gmail.com> References: <1359101996-11667-1-git-send-email-dantesu@gmail.com> In-Reply-To: <1358490506-18281-1-git-send-email-dantesu@faraday-tech.com> References: <1358490506-18281-1-git-send-email-dantesu@faraday-tech.com> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x [fuzzy] X-Received-From: 209.85.220.41 Cc: peter.maydell@linaro.org, blauwirbel@gmail.com, paul@codesourcery.com, Kuo-Jung Su , afaerber@suse.de, fred.konrad@greensocs.com Subject: [Qemu-devel] [PATCH v2 17/20] arm: add Faraday FTPWMTMR010 timer support X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org From: Kuo-Jung Su The FTPWMTMR010 is an APB device which provides up to 8 independent timers. Each timer can use either the internal system clock (The PCLK of APB) or external clock. These timers can be used to generate internal interrupts to the CPU. They can also be used to trigger DMA transfers. In addition, each timer supports PWM (Pulse Width Modulation) function, which can generate PWM signals for motor control or power level control. Signed-off-by: Kuo-Jung Su --- hw/ftpwmtmr010.c | 244 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 244 insertions(+) create mode 100644 hw/ftpwmtmr010.c diff --git a/hw/ftpwmtmr010.c b/hw/ftpwmtmr010.c new file mode 100644 index 0000000..0857523 --- /dev/null +++ b/hw/ftpwmtmr010.c @@ -0,0 +1,244 @@ +/* + * Faraday FTPWMTMR010 Timer. + * + * Copyright (c) 2012 Faraday Technology + * Written by Dante Su + * + * This code is licensed under GPL v2. + */ + +#include "hw.h" +#include "qdev.h" +#include "qemu/timer.h" +#include "sysemu/sysemu.h" +#include "sysbus.h" + +#define TYPE_FTPWMTMR010 "ftpwmtmr010" +#define TYPE_FTPWMTMR010_TIMER "ftpwmtmr010_timer" + +typedef struct Ftpwmtmr010State ftpwmtmr010_state; + +typedef struct Ftpwmtmr010Timer { + uint32_t ctrl; + uint32_t cntb; + int id; + uint64_t timeout; + uint64_t countdown; + qemu_irq irq; + QEMUTimer *qtimer; + ftpwmtmr010_state *chip; +} ftpwmtmr010_timer; + +struct Ftpwmtmr010State { + SysBusDevice busdev; + + MemoryRegion iomem; + ftpwmtmr010_timer timer[8]; + uint32_t freq; /* desired source clock */ + uint32_t step; /* get_ticks_per_sec() / freq */ + uint32_t stat; +}; + +#define FTPWMTMR010(obj) \ + OBJECT_CHECK(ftpwmtmr010_state, obj, TYPE_FTPWMTMR010) + +static uint64_t ftpwmtmr010_mem_read(void *opaque, hwaddr addr, unsigned size) +{ + ftpwmtmr010_state *s = FTPWMTMR010(opaque); + ftpwmtmr010_timer *t; + uint64_t now = qemu_get_clock_ns(vm_clock); + uint64_t ret = 0; + + if (addr == 0x00) { + ret = s->stat; + } else if (addr >= 0x10 && addr < 0x90) { + t = s->timer + (addr >> 4) - 1; + switch (addr & 0x0f) { + case 0x00: + return t->ctrl; + case 0x04: + return t->cntb; + case 0x0c: + if ((t->ctrl & 0x02) && (t->timeout > now)) { + ret = (t->timeout - now) / s->step; + } + break; + } + } + + return ret; +} + +static void ftpwmtmr010_mem_write(void *opaque, + hwaddr addr, + uint64_t val, + unsigned size) +{ + ftpwmtmr010_state *s = FTPWMTMR010(opaque); + ftpwmtmr010_timer *t; + + if (addr == 0x00) { + int i; + s->stat &= ~((uint32_t)val & 0xffffffff); + for (i = 0; i < 8; ++i) { + if (val & (1 << i)) { + qemu_irq_lower(s->timer[i].irq); + } + } + } else if (addr >= 0x10 && addr < 0x90) { + t = s->timer + (addr >> 4) - 1; + switch (addr & 0x0f) { + case 0x00: + t->ctrl = (uint32_t)val; + if (t->ctrl & (1 << 2)) { + t->countdown = (uint64_t)t->cntb * (uint64_t)s->step; + } + if (t->ctrl & (1 << 1)) { + t->timeout = t->countdown + qemu_get_clock_ns(vm_clock); + qemu_mod_timer(t->qtimer, t->timeout); + } + break; + case 0x04: + t->cntb = (uint32_t)val; + break; + } + } +} + +static const MemoryRegionOps ftpwmtmr010_ops = { + .read = ftpwmtmr010_mem_read, + .write = ftpwmtmr010_mem_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void ftpwmtmr010_timer_tick(void *opaque) +{ + ftpwmtmr010_timer *t = opaque; + ftpwmtmr010_state *s = t->chip; + + /* if the timer has been enabled/started */ + if (!(t->ctrl & (1 << 1))) { + return; + } + + /* if the interrupt enabled */ + if (t->ctrl & (1 << 5)) { + s->stat |= 1 << t->id; + if (t->ctrl & (1 << 6)) { + qemu_irq_pulse(t->irq); + } else { + qemu_irq_raise(t->irq); + } + } + + /* if auto-reload is enabled */ + if (t->ctrl & (1 << 4)) { + t->timeout = t->countdown + qemu_get_clock_ns(vm_clock); + qemu_mod_timer(t->qtimer, t->timeout); + } else { + t->ctrl &= ~(1 << 1); + } +} + +static void ftpwmtmr010_reset(DeviceState *ds) +{ + SysBusDevice *busdev = SYS_BUS_DEVICE(ds); + ftpwmtmr010_state *s = FTPWMTMR010(FROM_SYSBUS(ftpwmtmr010_state, busdev)); + int i; + + s->stat = 0; + + for (i = 0; i < 8; ++i) { + s->timer[i].ctrl = 0; + s->timer[i].cntb = 0; + s->timer[i].timeout = 0; + qemu_irq_lower(s->timer[i].irq); + qemu_del_timer(s->timer[i].qtimer); + } +} + +static int ftpwmtmr010_init(SysBusDevice *busdev) +{ + ftpwmtmr010_state *s = FTPWMTMR010(FROM_SYSBUS(ftpwmtmr010_state, busdev)); + int i; + + s->step = (uint64_t)get_ticks_per_sec() / (uint64_t)s->freq; + + printf("qemu: ftpwmtmr010 freq=%d\n", s->freq); + + memory_region_init_io(&s->iomem, + &ftpwmtmr010_ops, + s, + TYPE_FTPWMTMR010, + 0x1000); + sysbus_init_mmio(busdev, &s->iomem); + for (i = 0; i < 8; ++i) { + s->timer[i].id = i; + s->timer[i].chip = s; + s->timer[i].qtimer = qemu_new_timer_ns( + vm_clock, + ftpwmtmr010_timer_tick, + &s->timer[i]); + sysbus_init_irq(busdev, &s->timer[i].irq); + } + + return 0; +} + +static const VMStateDescription vmstate_ftpwmtmr010_timer = { + .name = TYPE_FTPWMTMR010_TIMER, + .version_id = 2, + .minimum_version_id = 2, + .minimum_version_id_old = 2, + .fields = (VMStateField[]) { + VMSTATE_UINT32(ctrl, ftpwmtmr010_timer), + VMSTATE_UINT32(cntb, ftpwmtmr010_timer), + VMSTATE_END_OF_LIST(), + }, +}; + +static const VMStateDescription vmstate_ftpwmtmr010 = { + .name = TYPE_FTPWMTMR010, + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(stat, ftpwmtmr010_state), + VMSTATE_UINT32(freq, ftpwmtmr010_state), + VMSTATE_UINT32(step, ftpwmtmr010_state), + VMSTATE_STRUCT_ARRAY(timer, ftpwmtmr010_state, 8, 1, + vmstate_ftpwmtmr010_timer, ftpwmtmr010_timer), + VMSTATE_END_OF_LIST(), + } +}; + +static Property ftpwmtmr010_properties[] = { + DEFINE_PROP_UINT32("freq", ftpwmtmr010_state, freq, 66000000), + DEFINE_PROP_END_OF_LIST(), +}; + +static void ftpwmtmr010_class_init(ObjectClass *klass, void *data) +{ + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + k->init = ftpwmtmr010_init; + dc->vmsd = &vmstate_ftpwmtmr010; + dc->props = ftpwmtmr010_properties; + dc->reset = ftpwmtmr010_reset; + dc->no_user = 1; +} + +static const TypeInfo ftpwmtmr010_info = { + .name = TYPE_FTPWMTMR010, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(ftpwmtmr010_state), + .class_init = ftpwmtmr010_class_init, +}; + +static void ftpwmtmr010_register_types(void) +{ + type_register_static(&ftpwmtmr010_info); +} + +type_init(ftpwmtmr010_register_types)