From nobody Sun May 19 01:15:20 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1550247323738241.58971459775216; Fri, 15 Feb 2019 08:15:23 -0800 (PST) Received: from localhost ([127.0.0.1]:42258 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gug92-0001IG-Mk for importer@patchew.org; Fri, 15 Feb 2019 11:15:20 -0500 Received: from eggs.gnu.org ([209.51.188.92]:47398) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1guftb-0006K5-O8 for qemu-devel@nongnu.org; Fri, 15 Feb 2019 10:59:25 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1guftS-00048U-9p for qemu-devel@nongnu.org; Fri, 15 Feb 2019 10:59:18 -0500 Received: from cock.li ([185.100.85.212]:34052) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1guftH-0003lj-GU; Fri, 15 Feb 2019 10:59:04 -0500 Date: Fri, 15 Feb 2019 17:59:20 +0200 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=airmail.cc; s=mail; t=1550246339; bh=rOySOWPs7Us7/aeiariZe4tMQWxSzV1r8CQYIqlY1Bg=; h=Date:From:To:Cc:Subject:From; b=gLIsilbkVNygYdeffIO0HZ5ps5hqf6LHBcDLq3DoIlB4YbdYwTSOqPupKtiuhUYW+ RnmPI2ncYY0KQUsI54ElJLVW8xX9GGfK5JZnglmE9uHO5xB/fO/EQ0fP5FDTuzFdo0 JxLkX3+UYCLEuwcjrm30yN9RC9suH7lhP2eVo4cQg131MQ9kvd/rHGDpo3J+G6t22k BDmou/j0Vs8azIY8bWSfXiYOTVkCx4yWV4CndXt66exLKiMGBeLQ/96p8M3CltqZjW zB/sXaZd3IqeGu4F6EAwlSKlWdKTqf0mougZ94/yC8hYD8lf703cBeeidP/22lc7Nb 39cZxKGmVp1/g== From: Mark To: qemu-devel@nongnu.org Message-ID: <20190215155919.GA10823@nyan> MIME-Version: 1.0 Content-Disposition: inline User-Agent: Mutt/1.10.1 (2018-07-13) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 185.100.85.212 Subject: [Qemu-devel] [v4 PATCH] hw/arm/bcm2835_peripherals: add bcm283x arm timer X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Peter Maydell , "open list:Raspberry Pi" , Philippe =?iso-8859-1?Q?Mathieu-Daud=E9?= , Andrew Baumann , "open list:All patches CC here" Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail-DKIM: fail (Header signature does not verify) Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Signed-off-by: Mark --- hw/arm/bcm2835_peripherals.c | 17 ++ hw/timer/Makefile.objs | 2 + hw/timer/bcm283x_timer.c | 271 +++++++++++++++++++++++++++ include/hw/arm/bcm2835_peripherals.h | 2 + include/hw/timer/bcm283x_timer.h | 50 +++++ 5 files changed, 342 insertions(+) create mode 100644 hw/timer/bcm283x_timer.c create mode 100644 include/hw/timer/bcm283x_timer.h diff --git a/hw/arm/bcm2835_peripherals.c b/hw/arm/bcm2835_peripherals.c index 6be7660e8c..e6d4d35496 100644 --- a/hw/arm/bcm2835_peripherals.c +++ b/hw/arm/bcm2835_peripherals.c @@ -117,6 +117,10 @@ static void bcm2835_peripherals_init(Object *obj) OBJECT(&s->sdhci.sdbus), &error_abort); object_property_add_const_link(OBJECT(&s->gpio), "sdbus-sdhost", OBJECT(&s->sdhost.sdbus), &error_abort); + + /* SP804-alike ARM Timer */ + sysbus_init_child_obj(obj, "bcm283x_timer", OBJECT(&s->bcm283x_timer), + sizeof(s->bcm283x_timer), TYPE_BCM283xTimer); } static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp) @@ -334,6 +338,19 @@ static void bcm2835_peripherals_realize(DeviceState *d= ev, Error **errp) error_propagate(errp, err); return; } + + /* SP804-alike ARM Timer */ + object_property_set_bool(OBJECT(&s->bcm283x_timer), true, "realized", = &err); + if (err) { + error_propagate(errp, err); + return; + } + + memory_region_add_subregion(&s->peri_mr, ARMCTRL_TIMER0_1_OFFSET, + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->bcm283x_timer), = 0)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->bcm283x_timer), 0, + qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_ARM_IRQ, + INTERRUPT_ARM_TIMER)); } static void bcm2835_peripherals_class_init(ObjectClass *oc, void *data) diff --git a/hw/timer/Makefile.objs b/hw/timer/Makefile.objs index 0e9a4530f8..09a3706701 100644 --- a/hw/timer/Makefile.objs +++ b/hw/timer/Makefile.objs @@ -47,3 +47,5 @@ common-obj-$(CONFIG_SUN4V_RTC) +=3D sun4v-rtc.o common-obj-$(CONFIG_CMSDK_APB_TIMER) +=3D cmsdk-apb-timer.o common-obj-$(CONFIG_CMSDK_APB_DUALTIMER) +=3D cmsdk-apb-dualtimer.o common-obj-$(CONFIG_MSF2) +=3D mss-timer.o + +common-obj-$(CONFIG_RASPI) +=3D bcm283x_timer.o diff --git a/hw/timer/bcm283x_timer.c b/hw/timer/bcm283x_timer.c new file mode 100644 index 0000000000..0f696ae61f --- /dev/null +++ b/hw/timer/bcm283x_timer.c @@ -0,0 +1,271 @@ +/* + * Broadcom BCM283x ARM timer variant based on ARM SP804 + * Copyright (c) 2019, Mark + * + * 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 "qemu/osdep.h" +#include "qemu/timer.h" +#include "qemu-common.h" +#include "hw/qdev.h" +#include "hw/timer/bcm283x_timer.h" +#include "qemu/main-loop.h" +#include "qemu/log.h" + +#define TIMER_CTRL_32BIT (1 << 1) +#define TIMER_CTRL_DIV1 (0 << 2) +#define TIMER_CTRL_DIV16 (1 << 2) +#define TIMER_CTRL_DIV256 (2 << 2) +#define TIMER_CTRL_IE (1 << 5) +#define TIMER_CTRL_ENABLE (1 << 7) +#define TIMER_CTRL_ENABLE_FREECNTR (1 << 9) + +/* BCM283x's implementation of SP804 ARM timer */ + +static void bcm283x_timer_set_irq(void *opaque, int irq, int level) +{ + BCM283xTimerState *s =3D BCM283xTimer(opaque); + + s->int_level =3D level; + qemu_set_irq(s->irq, s->int_level); +} + +static void bcm283x_timer_update(BCM283xTimerState *s) +{ + if (s->int_level && (s->control & TIMER_CTRL_IE)) { + qemu_irq_raise(s->irq); + } else { + qemu_irq_lower(s->irq); + } +} + +static void bcm283x_timer_tick(void *opaque) +{ + BCM283xTimerState *s =3D BCM283xTimer(opaque); + s->int_level =3D 1; + bcm283x_timer_update(s); +} + +static void bcm283x_free_timer_tick(void *opaque) +{ + /* Do nothing */ +} + +static uint64_t bcm283x_timer_read(void *opaque, hwaddr offset, unsigned s= ize) +{ + BCM283xTimerState *s =3D BCM283xTimer(opaque); + + switch (offset >> 2) { + case 0: /* Load register */ + case 6: /* Reload register */ + return s->limit; + case 1: /* Value register */ + return ptimer_get_count(s->timer); + case 2: /* Control register */ + return s->control; + case 3: /* IRQ clear/ACK register */ + /* + * The register is write-only, + * but returns reverse "ARMT" string bytes + */ + return 0x544D5241; + case 4: /* RAW IRQ register */ + return s->int_level; + case 5: /* Masked IRQ register */ + if ((s->control & TIMER_CTRL_IE) =3D=3D 0) { + return 0; + } + return s->int_level; + case 8: /* Free-running counter */ + return ptimer_get_count(s->free_timer); + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad offset %x\n", __func__, (int) offset); + return 0; + } +} + +static void bcm283x_timer_write(void *opaque, hwaddr offset, uint64_t valu= e, + unsigned size) +{ + BCM283xTimerState *s =3D BCM283xTimer(opaque); + uint32_t freq, freecntr_freq; + + switch (offset >> 2) { + case 0: /* Load register */ + s->limit =3D value; + ptimer_set_limit(s->timer, s->limit, 1); + break; + case 1: /* Value register */ + /* Read only */ + break; + case 2: /* Control register */ + if (s->control & TIMER_CTRL_ENABLE) { + ptimer_stop(s->timer); + } + + s->control =3D value; + + /* Configure SP804 */ + freq =3D BCM283x_SYSTEM_CLOCK_FREQ / (s->prediv + 1); + /* Set pre-scaler */ + switch ((value >> 2) & 3) { + case 1: /* 16 */ + freq >>=3D 4; + break; + case 2: /* 256 */ + freq >>=3D 8; + break; + } + ptimer_set_limit(s->timer, s->limit, s->control & TIMER_CTRL_ENABL= E); + ptimer_set_freq(s->timer, freq); + + /* Configure free-running counter */ + freecntr_freq =3D BCM283x_SYSTEM_CLOCK_FREQ / + (1 + ((value >> 16) & 0xFF)); + if (s->control & TIMER_CTRL_32BIT) { + ptimer_set_limit(s->free_timer, 0xFFFFFFFF, + s->control & TIMER_CTRL_ENABLE_FREECNTR); + } else { + ptimer_set_limit(s->free_timer, 0xFFFF, + s->control & TIMER_CTRL_ENABLE_FREECNTR); + } + ptimer_set_freq(s->free_timer, freecntr_freq); + + if (s->control & TIMER_CTRL_ENABLE) { + ptimer_run(s->timer, 0); + } else { + ptimer_stop(s->free_timer); + } + + if (s->control & TIMER_CTRL_ENABLE_FREECNTR) { + ptimer_run(s->free_timer, 0); + } else { + ptimer_stop(s->free_timer); + } + break; + case 3: /* IRQ clear/ACK register */ + s->int_level =3D 0; + break; + case 6: /* Reload register */ + s->limit =3D value; + ptimer_set_limit(s->timer, s->limit, 0); + break; + case 7: /* Pre-divider register */ + s->prediv =3D value; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad offset %x\n", __func__, (int) offset); + break; + } + + bcm283x_timer_update(s); +} + +static const MemoryRegionOps bcm283x_timer_ops =3D { + .read =3D bcm283x_timer_read, + .write =3D bcm283x_timer_write, + .endianness =3D DEVICE_NATIVE_ENDIAN +}; + +static const VMStateDescription vmstate_bcm283x_timer =3D { + .name =3D "bcm283x_timer", + .version_id =3D 1, + .minimum_version_id =3D 1, + .fields =3D (VMStateField[]) { + VMSTATE_UINT32(control, BCM283xTimerState), + VMSTATE_UINT32(limit, BCM283xTimerState), + VMSTATE_UINT32(int_level, BCM283xTimerState), + VMSTATE_PTIMER(timer, BCM283xTimerState), + VMSTATE_PTIMER(free_timer, BCM283xTimerState), + VMSTATE_END_OF_LIST() + } +}; + +static void bcm283x_timer_init(Object *obj) +{ + BCM283xTimerState *s =3D BCM283xTimer(obj); + SysBusDevice *sbd =3D SYS_BUS_DEVICE(obj); + + memory_region_init_io(&s->iomem, obj, &bcm283x_timer_ops, s, + TYPE_BCM283xTimer, 0x100); + + sysbus_init_mmio(sbd, &s->iomem); + sysbus_init_irq(sbd, &s->irq); +} + +static void bcm283x_timer_reset(DeviceState *dev) +{ + BCM283xTimerState *s =3D BCM283xTimer(dev); + + s->limit =3D 0; + s->int_level =3D 0; + s->control =3D TIMER_CTRL_IE | (0x0E << 16); + s->prediv =3D 0x7D; + + /* + * Stop the timers. + * No need to update freqs/limits as this will automatically be done o= nce + * the system writes control register. + */ + ptimer_stop(s->timer); + ptimer_stop(s->free_timer); +} + +static void bcm283x_timer_realize(DeviceState *dev, Error **errp) +{ + BCM283xTimerState *s =3D BCM283xTimer(dev); + QEMUBH *bh; + + s->limit =3D 0; + s->int_level =3D 0; + s->control =3D TIMER_CTRL_IE | (0x0E << 16); + s->prediv =3D 0x7D; + + /* Create a regular SP804 timer */ + bh =3D qemu_bh_new(bcm283x_timer_tick, s); + s->timer =3D ptimer_init(bh, PTIMER_POLICY_DEFAULT); + s->irq =3D qemu_allocate_irq(bcm283x_timer_set_irq, s, 0); + + /* Create a free-running timer */ + bh =3D qemu_bh_new(bcm283x_free_timer_tick, s); + s->free_timer =3D ptimer_init(bh, PTIMER_POLICY_DEFAULT); + + vmstate_register(NULL, -1, &vmstate_bcm283x_timer, s); +} + +static void bcm283x_timer_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *k =3D DEVICE_CLASS(klass); + + k->realize =3D bcm283x_timer_realize; + k->vmsd =3D &vmstate_bcm283x_timer; + k->reset =3D bcm283x_timer_reset; +} + +static const TypeInfo bcm283x_timer_info =3D { + .name =3D TYPE_BCM283xTimer, + .parent =3D TYPE_SYS_BUS_DEVICE, + .instance_size =3D sizeof(BCM283xTimerState), + .instance_init =3D bcm283x_timer_init, + .class_init =3D bcm283x_timer_class_init +}; + +static void bcm283x_timer_register_types(void) +{ + type_register_static(&bcm283x_timer_info); +} + +type_init(bcm283x_timer_register_types) diff --git a/include/hw/arm/bcm2835_peripherals.h b/include/hw/arm/bcm2835_= peripherals.h index f5b193f670..bbe01d1500 100644 --- a/include/hw/arm/bcm2835_peripherals.h +++ b/include/hw/arm/bcm2835_peripherals.h @@ -23,6 +23,7 @@ #include "hw/sd/sdhci.h" #include "hw/sd/bcm2835_sdhost.h" #include "hw/gpio/bcm2835_gpio.h" +#include "hw/timer/bcm283x_timer.h" #define TYPE_BCM2835_PERIPHERALS "bcm2835-peripherals" #define BCM2835_PERIPHERALS(obj) \ @@ -48,6 +49,7 @@ typedef struct BCM2835PeripheralState { SDHCIState sdhci; BCM2835SDHostState sdhost; BCM2835GpioState gpio; + BCM283xTimerState bcm283x_timer; } BCM2835PeripheralState; #endif /* BCM2835_PERIPHERALS_H */ diff --git a/include/hw/timer/bcm283x_timer.h b/include/hw/timer/bcm283x_ti= mer.h new file mode 100644 index 0000000000..99d0ae0f61 --- /dev/null +++ b/include/hw/timer/bcm283x_timer.h @@ -0,0 +1,50 @@ +/* + * Broadcom BCM283x ARM timer variant based on ARM SP804 + * Copyright (c) 2019, Mark + * + * 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 . + */ +#ifndef HW_TIMER_BCM2835_TIMER_H +#define HW_TIMER_BCM2835_TIMER_H + +#include "hw/sysbus.h" +#include "hw/ptimer.h" + +/* + * The datasheet stated 252MHz is the system clock value after reset, + * but it may be changed either by device going to sleep mode or + * by kernel configuration + */ +#define BCM283x_SYSTEM_CLOCK_FREQ 252000000 + +#define TYPE_BCM283xTimer "bcm283x_timer" +#define BCM283xTimer(obj) \ + OBJECT_CHECK(BCM283xTimerState, (obj), TYPE_BCM283xTimer) + +typedef struct { + SysBusDevice parent_obj; + MemoryRegion iomem; + + qemu_irq irq; + + uint32_t control; + uint32_t limit; + uint32_t int_level; + uint32_t prediv; + + ptimer_state *timer; + ptimer_state *free_timer; +} BCM283xTimerState; + +#endif -- 2.20.1