From patchwork Wed Sep 12 11:49:18 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Forsgren X-Patchwork-Id: 183350 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 067762C0089 for ; Wed, 12 Sep 2012 22:10:17 +1000 (EST) Received: from localhost ([::1]:41118 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TBlln-0005Kk-1x for incoming@patchwork.ozlabs.org; Wed, 12 Sep 2012 08:10:15 -0400 Received: from eggs.gnu.org ([208.118.235.92]:33141) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TBllT-0005Gb-CK for qemu-devel@nongnu.org; Wed, 12 Sep 2012 08:10:05 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1TBllM-0002KJ-HQ for qemu-devel@nongnu.org; Wed, 12 Sep 2012 08:09:55 -0400 Received: from sestofw01.enea.se ([192.36.1.252]:15167 helo=mx-3.enea.com) by eggs.gnu.org with smtp (Exim 4.71) (envelope-from ) id 1TBllM-0002K0-31 for qemu-devel@nongnu.org; Wed, 12 Sep 2012 08:09:48 -0400 Received: from SESTOEX09.enea.se ([fe80::cc12:dcdc:d521:97f8]) by SESTOEX08.enea.se ([fe80::fda4:d8b2:80c9:b320%24]) with mapi id 14.02.0298.004; Wed, 12 Sep 2012 13:49:18 +0200 From: Daniel Forsgren To: "qemu-devel@nongnu.org" Thread-Topic: [PATCH] Basic support for ARM A15 "architectured" (cp15) timers Thread-Index: Ac2Q21cl/ImTkDkhQlC59oRB+qyBIQ== Date: Wed, 12 Sep 2012 11:49:18 +0000 Message-ID: Accept-Language: sv-SE, en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: x-originating-ip: [172.16.140.72] MIME-Version: 1.0 X-detected-operating-system: by eggs.gnu.org: Windows XP/2000 (RFC1323+, w+, tstamp-) X-Received-From: 192.36.1.252 Subject: [Qemu-devel] [PATCH] Basic support for ARM A15 "architectured" (cp15) timers 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 This patch adds basic support for the "architected" timers (i.e. cp15) found in A15. It's enough to allow Linux to boot, using arch_timer for the tick. However - it is not a complete model of the timer block at large, it is not that well structured, and it is currently tested with qemu-linaro-1.1.50-2012.07 (not latest and greatest). It's simply a prototype. However, if anyone wants to play with the architectured (cp15) timers instead of sp804, then please feel free to try it out. It has been tested with linux-linaro-3.6-rc2-2012.08, and you can easily verify the existence of these timers under /proc/interrupts: root@linaro-developer:~# cat /proc/interrupts cat /proc/interrupts CPU0 29: 7424 GIC arch_timer 30: 0 GIC arch_timer Please note that this also requires some minor fixes that are not part of qemu-linaro-1.1.50-2012.07: http://patches.linaro.org/9833/ Signed-off-by: Daniel Forsgren diff -Nupr qemu-linaro-1.1.50-2012.07/hw/a15mpcore.c qemu-linaro-1.1.50-2012.07-modified/hw/a15mpcore.c --- qemu-linaro-1.1.50-2012.07/hw/a15mpcore.c 2012-07-05 16:48:28.000000000 +0200 +++ qemu-linaro-1.1.50-2012.07-modified/hw/a15mpcore.c 2012-09-12 11:24:25.844237405 +0200 @@ -28,6 +28,7 @@ typedef struct A15MPPrivState { uint32_t num_cpu; uint32_t num_irq; MemoryRegion container; + DeviceState *archtimer; DeviceState *gic; } A15MPPrivState; @@ -40,7 +41,8 @@ static void a15mp_priv_set_irq(void *opa static int a15mp_priv_init(SysBusDevice *dev) { A15MPPrivState *s = FROM_SYSBUS(A15MPPrivState, dev); - SysBusDevice *busdev; + SysBusDevice *busdev, *timerbusdev; + int i; if (kvm_irqchip_in_kernel()) { s->gic = qdev_create(NULL, "kvm-arm_gic"); @@ -60,6 +62,11 @@ static int a15mp_priv_init(SysBusDevice /* Pass through inbound GPIO lines to the GIC */ qdev_init_gpio_in(&s->busdev.qdev, a15mp_priv_set_irq, s->num_irq - 32); + s->archtimer = qdev_create(NULL, "arm_archtimer"); + // qdev_prop_set_uint32(s->archtimer, "num-cpu", s->num_cpu); + qdev_init_nofail(s->archtimer); + timerbusdev = sysbus_from_qdev(s->archtimer); + /* Memory map (addresses are offsets from PERIPHBASE): * 0x0000-0x0fff -- reserved * 0x1000-0x1fff -- GIC Distributor @@ -75,6 +82,16 @@ static int a15mp_priv_init(SysBusDevice sysbus_mmio_get_region(busdev, 1)); sysbus_init_mmio(dev, &s->container); + + + for (i = 0; i < s->num_cpu; i++) { + int ppibase = (s->num_irq - 32) + i * 32; + sysbus_connect_irq(timerbusdev, i * 2, + qdev_get_gpio_in(s->gic, ppibase + 29)); + sysbus_connect_irq(timerbusdev, i * 2 + 1, + qdev_get_gpio_in(s->gic, ppibase + 30)); + } + return 0; } diff -Nupr qemu-linaro-1.1.50-2012.07/hw/arm/Makefile.objs qemu-linaro-1.1.50-2012.07-modified/hw/arm/Makefile.objs --- qemu-linaro-1.1.50-2012.07/hw/arm/Makefile.objs 2012-07-05 16:48:28.000000000 +0200 +++ qemu-linaro-1.1.50-2012.07-modified/hw/arm/Makefile.objs 2012-09-12 11:28:39.121053287 +0200 @@ -1,4 +1,4 @@ -obj-y = integratorcp.o versatilepb.o arm_pic.o arm_timer.o +obj-y = integratorcp.o versatilepb.o arm_pic.o arm_timer.o arm_archtimer.o obj-y += arm_boot.o pl011.o pl031.o pl050.o pl080.o pl110.o pl181.o pl190.o obj-y += versatile_pci.o obj-y += versatile_i2c.o diff -Nupr qemu-linaro-1.1.50-2012.07/hw/arm_archtimer.c qemu-linaro-1.1.50-2012.07-modified/hw/arm_archtimer.c --- qemu-linaro-1.1.50-2012.07/hw/arm_archtimer.c 1970-01-01 01:00:00.000000000 +0100 +++ qemu-linaro-1.1.50-2012.07-modified/hw/arm_archtimer.c 2012-09-12 13:21:44.676267111 +0200 @@ -0,0 +1,232 @@ +/* + * "Architectured" timer for A15 + * + * Copyright (c) 2012 Enea Software AB + * Written by Daniel Forsgren + * + * 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 of the License, or (at your option) any later version. + * + * 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 "sysbus.h" +#include "qemu-timer.h" + +/* Primitive (and imcomplete) support for A15 "architectured" timers + + - Only PL1 timer supported. + + - Only minimal subset of fuctionality requred by Linux. + + - Only tested with single-core. + +*/ + +/* control register bit assignment */ +#define CTL_ENABLE 0x01 +#define CTL_MASK 0x02 +#define CTL_INT 0x04 + +/* state of per-core timers */ +typedef struct { + ARMCPU *cpu; /* who are we serving */ + QEMUTimer *pl1_timer; + QEMUTimer *pl2_timer; + qemu_irq pl1_irq; + qemu_irq pl2_irq; + uint32_t cntkctl; /* timer pl1 control register */ + uint32_t cntp_ctl; /* pl1 physical timer control register */ + uint64_t cntvoff; /* virtual offset register */ +} archtimers; + +#define MAX_CPUS 4 + +typedef struct { + SysBusDevice busdev; + archtimers archtimers[MAX_CPUS]; +} arm_archtimer_state; + + +static int a15_cntfrq_read(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t *value) +{ + /* Let's assume that we're running at 1GHz for now. It's not + correct, but it simplifies translation between cycles <-> ns */ + *value = 1000000000UL; + return 0; +} + +static int a15_cntkctl_read(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t *value) +{ + archtimers *at = (archtimers *)(ri->opaque); + *value = at->cntkctl; + return 0; +} + +static int a15_cntpct_read(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t *value) +{ + /* Let's assume that the physical count register is driven by + vm_clock for now. As we have specified that that we're running + at 1GHz, no translation from ns should be needed. */ + *value = qemu_get_clock_ns(vm_clock); + return 0; +} + +static int a15_cntvct_read(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t *value) +{ + archtimers *at = (archtimers *)(ri->opaque); + + /* Virtual count should subtract the virtual offfset from physical + count? */ + *value = qemu_get_clock_ns(vm_clock) - at->cntvoff; + return 0; +} + +static int a15_cntp_tval_read(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t *value) +{ + archtimers *at = (archtimers *)(ri->opaque); + *value = qemu_timer_expire_time_ns(at->pl1_timer); + return 0; +} + +static int a15_cntp_tval_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) +{ + archtimers *at = (archtimers *)(ri->opaque); + + /* I assume that setting a new value means that we should clear + any previous? */ + qemu_set_irq(at->pl1_irq, 0); + at->cntp_ctl &= ~CTL_INT; + + qemu_mod_timer_ns(at->pl1_timer, qemu_get_clock_ns(vm_clock) + value); + + return 0; +} + +static int a15_cntp_ctl_read(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t *value) +{ + archtimers *at = (archtimers *)(ri->opaque); + + *value = at->cntp_ctl; + + return 0; +} + +static int a15_cntp_ctl_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + archtimers *at = (archtimers *)(ri->opaque); + + /* Copy "enable" and "mask" bits from the new value. Preserve the rest. */ + at->cntp_ctl = (at->cntp_ctl & ~(CTL_ENABLE | CTL_MASK)) | (value & (CTL_ENABLE | CTL_MASK)); + + /* If no interrupt is asserted, or interrupt is masked, then lower the irq. */ + if (!(at->cntp_ctl & CTL_INT) || (at->cntp_ctl & CTL_MASK)) + qemu_set_irq(at->pl1_irq, 0); + + /* If interrupt is asserted and not masked, then raise the irq. */ + if ((at->cntp_ctl & CTL_INT) && !(at->cntp_ctl & CTL_MASK)) + qemu_set_irq(at->pl1_irq, 1); + + return 0; +} + +static const ARMCPRegInfo archtimer_cp_reginfo[] = { + + { .name = "CNTFRQ", .cp = 15, .crn = 14, .crm = 0, .opc1 = 0, .opc2 = 0, + .access = PL1_R, .resetvalue = 0, .readfn = a15_cntfrq_read, }, + + { .name = "CNTKCTL", .cp = 15, .crn = 14, .crm = 1, .opc1 = 0, .opc2 = 0, + .access = PL1_R, .resetvalue = 0, .readfn = a15_cntkctl_read, }, + + { .name = "CNTP_TVAL", .cp = 15, .crn = 14, .crm = 2, .opc1 = 0, .opc2 = 0, + .access = PL1_RW, .resetvalue = 0, .readfn = a15_cntp_tval_read, + .writefn = a15_cntp_tval_write, }, + + { .name = "CNTP_CTL", .cp = 15, .crn = 14, .crm = 2, .opc1 = 0, .opc2 = 1, + .access = PL1_RW, .resetvalue = 0, .readfn = a15_cntp_ctl_read, + .writefn = a15_cntp_ctl_write, }, + + { .name = "CNTPCT", .type = ARM_CP_64BIT, .cp = 15, .crn = 0, .crm = 14, .opc1 = 0, .opc2 = 0, + .access = PL1_R, .resetvalue = 0, .readfn = a15_cntpct_read, }, + + { .name = "CNTVCT", .type = ARM_CP_64BIT, .cp = 15, .crn = 0, .crm = 14, .opc1 = 1, .opc2 = 0, + .access = PL1_R, .resetvalue = 0, .readfn = a15_cntvct_read, }, + + REGINFO_SENTINEL +}; + +static void pl1_timer_cb(void *opaque) +{ + archtimers *at = (archtimers *)opaque; + + /* Set the interrupt bit in control register */ + at->cntp_ctl |= CTL_INT; + + /* If not masked, then raise the irq */ + if (!(at->cntp_ctl & CTL_MASK)) + qemu_set_irq(at->pl1_irq, 1); +} + +static int arm_archtimer_init(SysBusDevice *dev) +{ + arm_archtimer_state *s = FROM_SYSBUS(arm_archtimer_state, dev); + CPUArchState *cpu; + int i; + + for (cpu = first_cpu, i = 0; cpu; cpu = cpu->next_cpu, i++) { + archtimers *at = &s->archtimers[i]; + at->pl1_timer = qemu_new_timer_ns(vm_clock, &pl1_timer_cb, at); + sysbus_init_irq(dev, &(at->pl1_irq)); + sysbus_init_irq(dev, &(at->pl2_irq)); + s->archtimers[i].cpu = arm_env_get_cpu(cpu); + define_arm_cp_regs_with_opaque(s->archtimers[i].cpu, archtimer_cp_reginfo, (void *)at); + } + + return 0; +} + +static void arm_archtimer_reset(DeviceState *dev) +{ + arm_archtimer_state *s = + FROM_SYSBUS(arm_archtimer_state, sysbus_from_qdev(dev)); + int i; + + for (i = 0; i < MAX_CPUS; i++) { + if (s->archtimers[i].pl1_timer) + qemu_del_timer(s->archtimers[i].pl1_timer); + if (s->archtimers[i].pl2_timer) + qemu_del_timer(s->archtimers[i].pl2_timer); + } +} + +static void arm_archtimer_class_init(ObjectClass *class, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(class); + SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(class); + + sbc->init = arm_archtimer_init; + dc->reset = arm_archtimer_reset; +} + +static TypeInfo arm_archtimer_info = { + .name = "arm_archtimer", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(arm_archtimer_state), + .class_init = arm_archtimer_class_init, +}; + +static void arm_archtimer_register_types(void) +{ + type_register_static(&arm_archtimer_info); +} + +type_init(arm_archtimer_register_types) diff -Nupr qemu-linaro-1.1.50-2012.07/target-arm/helper.c qemu-linaro-1.1.50-2012.07-modified/target-arm/helper.c --- qemu-linaro-1.1.50-2012.07/target-arm/helper.c 2012-07-05 16:48:28.000000000 +0200 +++ qemu-linaro-1.1.50-2012.07-modified/target-arm/helper.c 2012-09-12 13:15:45.544424842 +0200 @@ -1012,9 +1012,11 @@ void register_cp_regs_for_features(ARMCP if (arm_feature(env, ARM_FEATURE_THUMB2EE)) { define_arm_cp_regs(cpu, t2ee_cp_reginfo); } + /* if (arm_feature(env, ARM_FEATURE_GENERIC_TIMER)) { define_arm_cp_regs(cpu, generic_timer_cp_reginfo); } + */ if (arm_feature(env, ARM_FEATURE_VAPA)) { define_arm_cp_regs(cpu, vapa_cp_reginfo); }