CONFIG_USB_MUSB=y
CONFIG_ARM_TIMER=y
+CONFIG_ARM_MPTIMER=y
CONFIG_PL011=y
CONFIG_PL022=y
CONFIG_PL031=y
CONFIG_BITBANG_I2C=y
CONFIG_FRAMEBUFFER=y
CONFIG_XILINX_SPIPS=y
+
CONFIG_MARVELL_88W8618=y
CONFIG_OMAP=y
+CONFIG_TSC210X=y
CONFIG_BLIZZARD=y
CONFIG_ONENAND=y
+CONFIG_TUSB6010=y
CONFIG_IMX=y
CONFIG_ZAURUS=y
CONFIG_PCNET_COMMON=y
CONFIG_LANCE=y
CONFIG_TCX=y
+CONFIG_SLAVIO=y
CONFIG_CS4231=y
CONFIG_GRLIB=y
obj-y += a9scu.o
obj-y += realview_gic.o arm_sysctl.o arm11mpcore.o a9mpcore.o
obj-y += exynos4210_gic.o exynos4210_combiner.o
-obj-y += exynos4210_pwm.o
-obj-y += exynos4210_pmu.o exynos4210_mct.o
-obj-y += exynos4210_rtc.o
-obj-y += arm_mptimer.o a15mpcore.o
+obj-y += exynos4210_pmu.o
+obj-y += a15mpcore.o
obj-y += armv7m_nvic.o
-obj-y += pxa2xx_timer.o pxa2xx_dma.o
-obj-y += pxa2xx_mmci.o pxa2xx_pcmcia.o pxa2xx_keypad.o
+obj-y += pxa2xx_dma.o
+obj-y += pxa2xx_mmci.o pxa2xx_pcmcia.o
obj-y += zaurus.o
obj-y += omap_dma.o omap_clk.o omap_mmc.o \
omap_gpio.o omap_intc.o
-obj-y += soc_dma.o omap_gptimer.o omap_synctimer.o \
+obj-y += soc_dma.o \
omap_gpmc.o omap_sdrc.o omap_tap.o omap_l4.o
-obj-y += tsc210x.o
-obj-y += cbus.o tusb6010.o
+obj-y += cbus.o
obj-y += mst_fpga.o
obj-y += strongarm.o
-obj-y += imx_ccm.o imx_timer.o imx_avic.o
+obj-y += imx_ccm.o imx_avic.o
obj-$(CONFIG_KVM) += kvm/arm_gic.o
obj-y := $(addprefix ../,$(obj-y))
+++ /dev/null
-/*
- * Private peripheral timer/watchdog blocks for ARM 11MPCore and A9MP
- *
- * Copyright (c) 2006-2007 CodeSourcery.
- * Copyright (c) 2011 Linaro Limited
- * Written by Paul Brook, Peter Maydell
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-#include "hw/sysbus.h"
-#include "qemu/timer.h"
-
-/* This device implements the per-cpu private timer and watchdog block
- * which is used in both the ARM11MPCore and Cortex-A9MP.
- */
-
-#define MAX_CPUS 4
-
-/* State of a single timer or watchdog block */
-typedef struct {
- uint32_t count;
- uint32_t load;
- uint32_t control;
- uint32_t status;
- int64_t tick;
- QEMUTimer *timer;
- qemu_irq irq;
- MemoryRegion iomem;
-} TimerBlock;
-
-typedef struct {
- SysBusDevice busdev;
- uint32_t num_cpu;
- TimerBlock timerblock[MAX_CPUS];
- MemoryRegion iomem;
-} ARMMPTimerState;
-
-static inline int get_current_cpu(ARMMPTimerState *s)
-{
- CPUState *cpu_single_cpu = ENV_GET_CPU(cpu_single_env);
-
- if (cpu_single_cpu->cpu_index >= s->num_cpu) {
- hw_error("arm_mptimer: num-cpu %d but this cpu is %d!\n",
- s->num_cpu, cpu_single_cpu->cpu_index);
- }
- return cpu_single_cpu->cpu_index;
-}
-
-static inline void timerblock_update_irq(TimerBlock *tb)
-{
- qemu_set_irq(tb->irq, tb->status);
-}
-
-/* Return conversion factor from mpcore timer ticks to qemu timer ticks. */
-static inline uint32_t timerblock_scale(TimerBlock *tb)
-{
- return (((tb->control >> 8) & 0xff) + 1) * 10;
-}
-
-static void timerblock_reload(TimerBlock *tb, int restart)
-{
- if (tb->count == 0) {
- return;
- }
- if (restart) {
- tb->tick = qemu_get_clock_ns(vm_clock);
- }
- tb->tick += (int64_t)tb->count * timerblock_scale(tb);
- qemu_mod_timer(tb->timer, tb->tick);
-}
-
-static void timerblock_tick(void *opaque)
-{
- TimerBlock *tb = (TimerBlock *)opaque;
- tb->status = 1;
- if (tb->control & 2) {
- tb->count = tb->load;
- timerblock_reload(tb, 0);
- } else {
- tb->count = 0;
- }
- timerblock_update_irq(tb);
-}
-
-static uint64_t timerblock_read(void *opaque, hwaddr addr,
- unsigned size)
-{
- TimerBlock *tb = (TimerBlock *)opaque;
- int64_t val;
- switch (addr) {
- case 0: /* Load */
- return tb->load;
- case 4: /* Counter. */
- if (((tb->control & 1) == 0) || (tb->count == 0)) {
- return 0;
- }
- /* Slow and ugly, but hopefully won't happen too often. */
- val = tb->tick - qemu_get_clock_ns(vm_clock);
- val /= timerblock_scale(tb);
- if (val < 0) {
- val = 0;
- }
- return val;
- case 8: /* Control. */
- return tb->control;
- case 12: /* Interrupt status. */
- return tb->status;
- default:
- return 0;
- }
-}
-
-static void timerblock_write(void *opaque, hwaddr addr,
- uint64_t value, unsigned size)
-{
- TimerBlock *tb = (TimerBlock *)opaque;
- int64_t old;
- switch (addr) {
- case 0: /* Load */
- tb->load = value;
- /* Fall through. */
- case 4: /* Counter. */
- if ((tb->control & 1) && tb->count) {
- /* Cancel the previous timer. */
- qemu_del_timer(tb->timer);
- }
- tb->count = value;
- if (tb->control & 1) {
- timerblock_reload(tb, 1);
- }
- break;
- case 8: /* Control. */
- old = tb->control;
- tb->control = value;
- if (((old & 1) == 0) && (value & 1)) {
- if (tb->count == 0 && (tb->control & 2)) {
- tb->count = tb->load;
- }
- timerblock_reload(tb, 1);
- }
- break;
- case 12: /* Interrupt status. */
- tb->status &= ~value;
- timerblock_update_irq(tb);
- break;
- }
-}
-
-/* Wrapper functions to implement the "read timer/watchdog for
- * the current CPU" memory regions.
- */
-static uint64_t arm_thistimer_read(void *opaque, hwaddr addr,
- unsigned size)
-{
- ARMMPTimerState *s = (ARMMPTimerState *)opaque;
- int id = get_current_cpu(s);
- return timerblock_read(&s->timerblock[id], addr, size);
-}
-
-static void arm_thistimer_write(void *opaque, hwaddr addr,
- uint64_t value, unsigned size)
-{
- ARMMPTimerState *s = (ARMMPTimerState *)opaque;
- int id = get_current_cpu(s);
- timerblock_write(&s->timerblock[id], addr, value, size);
-}
-
-static const MemoryRegionOps arm_thistimer_ops = {
- .read = arm_thistimer_read,
- .write = arm_thistimer_write,
- .valid = {
- .min_access_size = 4,
- .max_access_size = 4,
- },
- .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static const MemoryRegionOps timerblock_ops = {
- .read = timerblock_read,
- .write = timerblock_write,
- .valid = {
- .min_access_size = 4,
- .max_access_size = 4,
- },
- .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static void timerblock_reset(TimerBlock *tb)
-{
- tb->count = 0;
- tb->load = 0;
- tb->control = 0;
- tb->status = 0;
- tb->tick = 0;
- if (tb->timer) {
- qemu_del_timer(tb->timer);
- }
-}
-
-static void arm_mptimer_reset(DeviceState *dev)
-{
- ARMMPTimerState *s =
- FROM_SYSBUS(ARMMPTimerState, SYS_BUS_DEVICE(dev));
- int i;
- for (i = 0; i < ARRAY_SIZE(s->timerblock); i++) {
- timerblock_reset(&s->timerblock[i]);
- }
-}
-
-static int arm_mptimer_init(SysBusDevice *dev)
-{
- ARMMPTimerState *s = FROM_SYSBUS(ARMMPTimerState, dev);
- int i;
- if (s->num_cpu < 1 || s->num_cpu > MAX_CPUS) {
- hw_error("%s: num-cpu must be between 1 and %d\n", __func__, MAX_CPUS);
- }
- /* We implement one timer block per CPU, and expose multiple MMIO regions:
- * * region 0 is "timer for this core"
- * * region 1 is "timer for core 0"
- * * region 2 is "timer for core 1"
- * and so on.
- * The outgoing interrupt lines are
- * * timer for core 0
- * * timer for core 1
- * and so on.
- */
- memory_region_init_io(&s->iomem, &arm_thistimer_ops, s,
- "arm_mptimer_timer", 0x20);
- sysbus_init_mmio(dev, &s->iomem);
- for (i = 0; i < s->num_cpu; i++) {
- TimerBlock *tb = &s->timerblock[i];
- tb->timer = qemu_new_timer_ns(vm_clock, timerblock_tick, tb);
- sysbus_init_irq(dev, &tb->irq);
- memory_region_init_io(&tb->iomem, &timerblock_ops, tb,
- "arm_mptimer_timerblock", 0x20);
- sysbus_init_mmio(dev, &tb->iomem);
- }
-
- return 0;
-}
-
-static const VMStateDescription vmstate_timerblock = {
- .name = "arm_mptimer_timerblock",
- .version_id = 2,
- .minimum_version_id = 2,
- .fields = (VMStateField[]) {
- VMSTATE_UINT32(count, TimerBlock),
- VMSTATE_UINT32(load, TimerBlock),
- VMSTATE_UINT32(control, TimerBlock),
- VMSTATE_UINT32(status, TimerBlock),
- VMSTATE_INT64(tick, TimerBlock),
- VMSTATE_TIMER(timer, TimerBlock),
- VMSTATE_END_OF_LIST()
- }
-};
-
-static const VMStateDescription vmstate_arm_mptimer = {
- .name = "arm_mptimer",
- .version_id = 2,
- .minimum_version_id = 2,
- .fields = (VMStateField[]) {
- VMSTATE_STRUCT_VARRAY_UINT32(timerblock, ARMMPTimerState, num_cpu,
- 2, vmstate_timerblock, TimerBlock),
- VMSTATE_END_OF_LIST()
- }
-};
-
-static Property arm_mptimer_properties[] = {
- DEFINE_PROP_UINT32("num-cpu", ARMMPTimerState, num_cpu, 0),
- DEFINE_PROP_END_OF_LIST()
-};
-
-static void arm_mptimer_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass);
-
- sbc->init = arm_mptimer_init;
- dc->vmsd = &vmstate_arm_mptimer;
- dc->reset = arm_mptimer_reset;
- dc->no_user = 1;
- dc->props = arm_mptimer_properties;
-}
-
-static const TypeInfo arm_mptimer_info = {
- .name = "arm_mptimer",
- .parent = TYPE_SYS_BUS_DEVICE,
- .instance_size = sizeof(ARMMPTimerState),
- .class_init = arm_mptimer_class_init,
-};
-
-static void arm_mptimer_register_types(void)
-{
- type_register_static(&arm_mptimer_info);
-}
-
-type_init(arm_mptimer_register_types)
# IO blocks
obj-y += etraxfs_dma.o
obj-y += etraxfs_pic.o
-obj-y += etraxfs_timer.o
obj-y := $(addprefix ../,$(obj-y))
+++ /dev/null
-/*
- * QEMU ETRAX Timers
- *
- * Copyright (c) 2007 Edgar E. Iglesias, Axis Communications AB.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-#include "hw/sysbus.h"
-#include "sysemu/sysemu.h"
-#include "qemu/timer.h"
-#include "hw/ptimer.h"
-
-#define D(x)
-
-#define RW_TMR0_DIV 0x00
-#define R_TMR0_DATA 0x04
-#define RW_TMR0_CTRL 0x08
-#define RW_TMR1_DIV 0x10
-#define R_TMR1_DATA 0x14
-#define RW_TMR1_CTRL 0x18
-#define R_TIME 0x38
-#define RW_WD_CTRL 0x40
-#define R_WD_STAT 0x44
-#define RW_INTR_MASK 0x48
-#define RW_ACK_INTR 0x4c
-#define R_INTR 0x50
-#define R_MASKED_INTR 0x54
-
-struct etrax_timer {
- SysBusDevice busdev;
- MemoryRegion mmio;
- qemu_irq irq;
- qemu_irq nmi;
-
- QEMUBH *bh_t0;
- QEMUBH *bh_t1;
- QEMUBH *bh_wd;
- ptimer_state *ptimer_t0;
- ptimer_state *ptimer_t1;
- ptimer_state *ptimer_wd;
-
- int wd_hits;
-
- /* Control registers. */
- uint32_t rw_tmr0_div;
- uint32_t r_tmr0_data;
- uint32_t rw_tmr0_ctrl;
-
- uint32_t rw_tmr1_div;
- uint32_t r_tmr1_data;
- uint32_t rw_tmr1_ctrl;
-
- uint32_t rw_wd_ctrl;
-
- uint32_t rw_intr_mask;
- uint32_t rw_ack_intr;
- uint32_t r_intr;
- uint32_t r_masked_intr;
-};
-
-static uint64_t
-timer_read(void *opaque, hwaddr addr, unsigned int size)
-{
- struct etrax_timer *t = opaque;
- uint32_t r = 0;
-
- switch (addr) {
- case R_TMR0_DATA:
- r = ptimer_get_count(t->ptimer_t0);
- break;
- case R_TMR1_DATA:
- r = ptimer_get_count(t->ptimer_t1);
- break;
- case R_TIME:
- r = qemu_get_clock_ns(vm_clock) / 10;
- break;
- case RW_INTR_MASK:
- r = t->rw_intr_mask;
- break;
- case R_MASKED_INTR:
- r = t->r_intr & t->rw_intr_mask;
- break;
- default:
- D(printf ("%s %x\n", __func__, addr));
- break;
- }
- return r;
-}
-
-static void update_ctrl(struct etrax_timer *t, int tnum)
-{
- unsigned int op;
- unsigned int freq;
- unsigned int freq_hz;
- unsigned int div;
- uint32_t ctrl;
-
- ptimer_state *timer;
-
- if (tnum == 0) {
- ctrl = t->rw_tmr0_ctrl;
- div = t->rw_tmr0_div;
- timer = t->ptimer_t0;
- } else {
- ctrl = t->rw_tmr1_ctrl;
- div = t->rw_tmr1_div;
- timer = t->ptimer_t1;
- }
-
-
- op = ctrl & 3;
- freq = ctrl >> 2;
- freq_hz = 32000000;
-
- switch (freq)
- {
- case 0:
- case 1:
- D(printf ("extern or disabled timer clock?\n"));
- break;
- case 4: freq_hz = 29493000; break;
- case 5: freq_hz = 32000000; break;
- case 6: freq_hz = 32768000; break;
- case 7: freq_hz = 100000000; break;
- default:
- abort();
- break;
- }
-
- D(printf ("freq_hz=%d div=%d\n", freq_hz, div));
- ptimer_set_freq(timer, freq_hz);
- ptimer_set_limit(timer, div, 0);
-
- switch (op)
- {
- case 0:
- /* Load. */
- ptimer_set_limit(timer, div, 1);
- break;
- case 1:
- /* Hold. */
- ptimer_stop(timer);
- break;
- case 2:
- /* Run. */
- ptimer_run(timer, 0);
- break;
- default:
- abort();
- break;
- }
-}
-
-static void timer_update_irq(struct etrax_timer *t)
-{
- t->r_intr &= ~(t->rw_ack_intr);
- t->r_masked_intr = t->r_intr & t->rw_intr_mask;
-
- D(printf("%s: masked_intr=%x\n", __func__, t->r_masked_intr));
- qemu_set_irq(t->irq, !!t->r_masked_intr);
-}
-
-static void timer0_hit(void *opaque)
-{
- struct etrax_timer *t = opaque;
- t->r_intr |= 1;
- timer_update_irq(t);
-}
-
-static void timer1_hit(void *opaque)
-{
- struct etrax_timer *t = opaque;
- t->r_intr |= 2;
- timer_update_irq(t);
-}
-
-static void watchdog_hit(void *opaque)
-{
- struct etrax_timer *t = opaque;
- if (t->wd_hits == 0) {
- /* real hw gives a single tick before reseting but we are
- a bit friendlier to compensate for our slower execution. */
- ptimer_set_count(t->ptimer_wd, 10);
- ptimer_run(t->ptimer_wd, 1);
- qemu_irq_raise(t->nmi);
- }
- else
- qemu_system_reset_request();
-
- t->wd_hits++;
-}
-
-static inline void timer_watchdog_update(struct etrax_timer *t, uint32_t value)
-{
- unsigned int wd_en = t->rw_wd_ctrl & (1 << 8);
- unsigned int wd_key = t->rw_wd_ctrl >> 9;
- unsigned int wd_cnt = t->rw_wd_ctrl & 511;
- unsigned int new_key = value >> 9 & ((1 << 7) - 1);
- unsigned int new_cmd = (value >> 8) & 1;
-
- /* If the watchdog is enabled, they written key must match the
- complement of the previous. */
- wd_key = ~wd_key & ((1 << 7) - 1);
-
- if (wd_en && wd_key != new_key)
- return;
-
- D(printf("en=%d new_key=%x oldkey=%x cmd=%d cnt=%d\n",
- wd_en, new_key, wd_key, new_cmd, wd_cnt));
-
- if (t->wd_hits)
- qemu_irq_lower(t->nmi);
-
- t->wd_hits = 0;
-
- ptimer_set_freq(t->ptimer_wd, 760);
- if (wd_cnt == 0)
- wd_cnt = 256;
- ptimer_set_count(t->ptimer_wd, wd_cnt);
- if (new_cmd)
- ptimer_run(t->ptimer_wd, 1);
- else
- ptimer_stop(t->ptimer_wd);
-
- t->rw_wd_ctrl = value;
-}
-
-static void
-timer_write(void *opaque, hwaddr addr,
- uint64_t val64, unsigned int size)
-{
- struct etrax_timer *t = opaque;
- uint32_t value = val64;
-
- switch (addr)
- {
- case RW_TMR0_DIV:
- t->rw_tmr0_div = value;
- break;
- case RW_TMR0_CTRL:
- D(printf ("RW_TMR0_CTRL=%x\n", value));
- t->rw_tmr0_ctrl = value;
- update_ctrl(t, 0);
- break;
- case RW_TMR1_DIV:
- t->rw_tmr1_div = value;
- break;
- case RW_TMR1_CTRL:
- D(printf ("RW_TMR1_CTRL=%x\n", value));
- t->rw_tmr1_ctrl = value;
- update_ctrl(t, 1);
- break;
- case RW_INTR_MASK:
- D(printf ("RW_INTR_MASK=%x\n", value));
- t->rw_intr_mask = value;
- timer_update_irq(t);
- break;
- case RW_WD_CTRL:
- timer_watchdog_update(t, value);
- break;
- case RW_ACK_INTR:
- t->rw_ack_intr = value;
- timer_update_irq(t);
- t->rw_ack_intr = 0;
- break;
- default:
- printf ("%s " TARGET_FMT_plx " %x\n",
- __func__, addr, value);
- break;
- }
-}
-
-static const MemoryRegionOps timer_ops = {
- .read = timer_read,
- .write = timer_write,
- .endianness = DEVICE_LITTLE_ENDIAN,
- .valid = {
- .min_access_size = 4,
- .max_access_size = 4
- }
-};
-
-static void etraxfs_timer_reset(void *opaque)
-{
- struct etrax_timer *t = opaque;
-
- ptimer_stop(t->ptimer_t0);
- ptimer_stop(t->ptimer_t1);
- ptimer_stop(t->ptimer_wd);
- t->rw_wd_ctrl = 0;
- t->r_intr = 0;
- t->rw_intr_mask = 0;
- qemu_irq_lower(t->irq);
-}
-
-static int etraxfs_timer_init(SysBusDevice *dev)
-{
- struct etrax_timer *t = FROM_SYSBUS(typeof (*t), dev);
-
- t->bh_t0 = qemu_bh_new(timer0_hit, t);
- t->bh_t1 = qemu_bh_new(timer1_hit, t);
- t->bh_wd = qemu_bh_new(watchdog_hit, t);
- t->ptimer_t0 = ptimer_init(t->bh_t0);
- t->ptimer_t1 = ptimer_init(t->bh_t1);
- t->ptimer_wd = ptimer_init(t->bh_wd);
-
- sysbus_init_irq(dev, &t->irq);
- sysbus_init_irq(dev, &t->nmi);
-
- memory_region_init_io(&t->mmio, &timer_ops, t, "etraxfs-timer", 0x5c);
- sysbus_init_mmio(dev, &t->mmio);
- qemu_register_reset(etraxfs_timer_reset, t);
- return 0;
-}
-
-static void etraxfs_timer_class_init(ObjectClass *klass, void *data)
-{
- SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
-
- sdc->init = etraxfs_timer_init;
-}
-
-static const TypeInfo etraxfs_timer_info = {
- .name = "etraxfs,timer",
- .parent = TYPE_SYS_BUS_DEVICE,
- .instance_size = sizeof (struct etrax_timer),
- .class_init = etraxfs_timer_class_init,
-};
-
-static void etraxfs_timer_register_types(void)
-{
- type_register_static(&etraxfs_timer_info);
-}
-
-type_init(etraxfs_timer_register_types)
+++ /dev/null
-/*
- * Samsung exynos4210 Multi Core timer
- *
- * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd.
- * All rights reserved.
- *
- * Evgeny Voevodin <e.voevodin@samsung.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 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 <http://www.gnu.org/licenses/>.
- */
-
-/*
- * Global Timer:
- *
- * Consists of two timers. First represents Free Running Counter and second
- * is used to measure interval from FRC to nearest comparator.
- *
- * 0 UINT64_MAX
- * | timer0 |
- * | <-------------------------------------------------------------- |
- * | --------------------------------------------frc---------------> |
- * |______________________________________________|__________________|
- * CMP0 CMP1 CMP2 | CMP3
- * __| |_
- * | timer1 |
- * | -------------> |
- * frc CMPx
- *
- * Problem: when implementing global timer as is, overflow arises.
- * next_time = cur_time + period * count;
- * period and count are 64 bits width.
- * Lets arm timer for MCT_GT_COUNTER_STEP count and update internal G_CNT
- * register during each event.
- *
- * Problem: both timers need to be implemented using MCT_XT_COUNTER_STEP because
- * local timer contains two counters: TCNT and ICNT. TCNT == 0 -> ICNT--.
- * IRQ is generated when ICNT riches zero. Implementation where TCNT == 0
- * generates IRQs suffers from too frequently events. Better to have one
- * uint64_t counter equal to TCNT*ICNT and arm ptimer.c for a minimum(TCNT*ICNT,
- * MCT_GT_COUNTER_STEP); (yes, if target tunes ICNT * TCNT to be too low values,
- * there is no way to avoid frequently events).
- */
-
-#include "hw/sysbus.h"
-#include "qemu/timer.h"
-#include "qemu-common.h"
-#include "hw/ptimer.h"
-
-#include "hw/arm/exynos4210.h"
-
-//#define DEBUG_MCT
-
-#ifdef DEBUG_MCT
-#define DPRINTF(fmt, ...) \
- do { fprintf(stdout, "MCT: [%24s:%5d] " fmt, __func__, __LINE__, \
- ## __VA_ARGS__); } while (0)
-#else
-#define DPRINTF(fmt, ...) do {} while (0)
-#endif
-
-#define MCT_CFG 0x000
-#define G_CNT_L 0x100
-#define G_CNT_U 0x104
-#define G_CNT_WSTAT 0x110
-#define G_COMP0_L 0x200
-#define G_COMP0_U 0x204
-#define G_COMP0_ADD_INCR 0x208
-#define G_COMP1_L 0x210
-#define G_COMP1_U 0x214
-#define G_COMP1_ADD_INCR 0x218
-#define G_COMP2_L 0x220
-#define G_COMP2_U 0x224
-#define G_COMP2_ADD_INCR 0x228
-#define G_COMP3_L 0x230
-#define G_COMP3_U 0x234
-#define G_COMP3_ADD_INCR 0x238
-#define G_TCON 0x240
-#define G_INT_CSTAT 0x244
-#define G_INT_ENB 0x248
-#define G_WSTAT 0x24C
-#define L0_TCNTB 0x300
-#define L0_TCNTO 0x304
-#define L0_ICNTB 0x308
-#define L0_ICNTO 0x30C
-#define L0_FRCNTB 0x310
-#define L0_FRCNTO 0x314
-#define L0_TCON 0x320
-#define L0_INT_CSTAT 0x330
-#define L0_INT_ENB 0x334
-#define L0_WSTAT 0x340
-#define L1_TCNTB 0x400
-#define L1_TCNTO 0x404
-#define L1_ICNTB 0x408
-#define L1_ICNTO 0x40C
-#define L1_FRCNTB 0x410
-#define L1_FRCNTO 0x414
-#define L1_TCON 0x420
-#define L1_INT_CSTAT 0x430
-#define L1_INT_ENB 0x434
-#define L1_WSTAT 0x440
-
-#define MCT_CFG_GET_PRESCALER(x) ((x) & 0xFF)
-#define MCT_CFG_GET_DIVIDER(x) (1 << ((x) >> 8 & 7))
-
-#define GET_G_COMP_IDX(offset) (((offset) - G_COMP0_L) / 0x10)
-#define GET_G_COMP_ADD_INCR_IDX(offset) (((offset) - G_COMP0_ADD_INCR) / 0x10)
-
-#define G_COMP_L(x) (G_COMP0_L + (x) * 0x10)
-#define G_COMP_U(x) (G_COMP0_U + (x) * 0x10)
-
-#define G_COMP_ADD_INCR(x) (G_COMP0_ADD_INCR + (x) * 0x10)
-
-/* MCT bits */
-#define G_TCON_COMP_ENABLE(x) (1 << 2 * (x))
-#define G_TCON_AUTO_ICREMENT(x) (1 << (2 * (x) + 1))
-#define G_TCON_TIMER_ENABLE (1 << 8)
-
-#define G_INT_ENABLE(x) (1 << (x))
-#define G_INT_CSTAT_COMP(x) (1 << (x))
-
-#define G_CNT_WSTAT_L 1
-#define G_CNT_WSTAT_U 2
-
-#define G_WSTAT_COMP_L(x) (1 << 4 * (x))
-#define G_WSTAT_COMP_U(x) (1 << ((4 * (x)) + 1))
-#define G_WSTAT_COMP_ADDINCR(x) (1 << ((4 * (x)) + 2))
-#define G_WSTAT_TCON_WRITE (1 << 16)
-
-#define GET_L_TIMER_IDX(offset) ((((offset) & 0xF00) - L0_TCNTB) / 0x100)
-#define GET_L_TIMER_CNT_REG_IDX(offset, lt_i) \
- (((offset) - (L0_TCNTB + 0x100 * (lt_i))) >> 2)
-
-#define L_ICNTB_MANUAL_UPDATE (1 << 31)
-
-#define L_TCON_TICK_START (1)
-#define L_TCON_INT_START (1 << 1)
-#define L_TCON_INTERVAL_MODE (1 << 2)
-#define L_TCON_FRC_START (1 << 3)
-
-#define L_INT_CSTAT_INTCNT (1 << 0)
-#define L_INT_CSTAT_FRCCNT (1 << 1)
-
-#define L_INT_INTENB_ICNTEIE (1 << 0)
-#define L_INT_INTENB_FRCEIE (1 << 1)
-
-#define L_WSTAT_TCNTB_WRITE (1 << 0)
-#define L_WSTAT_ICNTB_WRITE (1 << 1)
-#define L_WSTAT_FRCCNTB_WRITE (1 << 2)
-#define L_WSTAT_TCON_WRITE (1 << 3)
-
-enum LocalTimerRegCntIndexes {
- L_REG_CNT_TCNTB,
- L_REG_CNT_TCNTO,
- L_REG_CNT_ICNTB,
- L_REG_CNT_ICNTO,
- L_REG_CNT_FRCCNTB,
- L_REG_CNT_FRCCNTO,
-
- L_REG_CNT_AMOUNT
-};
-
-#define MCT_NIRQ 6
-#define MCT_SFR_SIZE 0x444
-
-#define MCT_GT_CMP_NUM 4
-
-#define MCT_GT_MAX_VAL UINT64_MAX
-
-#define MCT_GT_COUNTER_STEP 0x100000000ULL
-#define MCT_LT_COUNTER_STEP 0x100000000ULL
-#define MCT_LT_CNT_LOW_LIMIT 0x100
-
-/* global timer */
-typedef struct {
- qemu_irq irq[MCT_GT_CMP_NUM];
-
- struct gregs {
- uint64_t cnt;
- uint32_t cnt_wstat;
- uint32_t tcon;
- uint32_t int_cstat;
- uint32_t int_enb;
- uint32_t wstat;
- uint64_t comp[MCT_GT_CMP_NUM];
- uint32_t comp_add_incr[MCT_GT_CMP_NUM];
- } reg;
-
- uint64_t count; /* Value FRC was armed with */
- int32_t curr_comp; /* Current comparator FRC is running to */
-
- ptimer_state *ptimer_frc; /* FRC timer */
-
-} Exynos4210MCTGT;
-
-/* local timer */
-typedef struct {
- int id; /* timer id */
- qemu_irq irq; /* local timer irq */
-
- struct tick_timer {
- uint32_t cnt_run; /* cnt timer is running */
- uint32_t int_run; /* int timer is running */
-
- uint32_t last_icnto;
- uint32_t last_tcnto;
- uint32_t tcntb; /* initial value for TCNTB */
- uint32_t icntb; /* initial value for ICNTB */
-
- /* for step mode */
- uint64_t distance; /* distance to count to the next event */
- uint64_t progress; /* progress when counting by steps */
- uint64_t count; /* count to arm timer with */
-
- ptimer_state *ptimer_tick; /* timer for tick counter */
- } tick_timer;
-
- /* use ptimer.c to represent count down timer */
-
- ptimer_state *ptimer_frc; /* timer for free running counter */
-
- /* registers */
- struct lregs {
- uint32_t cnt[L_REG_CNT_AMOUNT];
- uint32_t tcon;
- uint32_t int_cstat;
- uint32_t int_enb;
- uint32_t wstat;
- } reg;
-
-} Exynos4210MCTLT;
-
-typedef struct Exynos4210MCTState {
- SysBusDevice busdev;
- MemoryRegion iomem;
-
- /* Registers */
- uint32_t reg_mct_cfg;
-
- Exynos4210MCTLT l_timer[2];
- Exynos4210MCTGT g_timer;
-
- uint32_t freq; /* all timers tick frequency, TCLK */
-} Exynos4210MCTState;
-
-/*** VMState ***/
-static const VMStateDescription vmstate_tick_timer = {
- .name = "exynos4210.mct.tick_timer",
- .version_id = 1,
- .minimum_version_id = 1,
- .minimum_version_id_old = 1,
- .fields = (VMStateField[]) {
- VMSTATE_UINT32(cnt_run, struct tick_timer),
- VMSTATE_UINT32(int_run, struct tick_timer),
- VMSTATE_UINT32(last_icnto, struct tick_timer),
- VMSTATE_UINT32(last_tcnto, struct tick_timer),
- VMSTATE_UINT32(tcntb, struct tick_timer),
- VMSTATE_UINT32(icntb, struct tick_timer),
- VMSTATE_UINT64(distance, struct tick_timer),
- VMSTATE_UINT64(progress, struct tick_timer),
- VMSTATE_UINT64(count, struct tick_timer),
- VMSTATE_PTIMER(ptimer_tick, struct tick_timer),
- VMSTATE_END_OF_LIST()
- }
-};
-
-static const VMStateDescription vmstate_lregs = {
- .name = "exynos4210.mct.lregs",
- .version_id = 1,
- .minimum_version_id = 1,
- .minimum_version_id_old = 1,
- .fields = (VMStateField[]) {
- VMSTATE_UINT32_ARRAY(cnt, struct lregs, L_REG_CNT_AMOUNT),
- VMSTATE_UINT32(tcon, struct lregs),
- VMSTATE_UINT32(int_cstat, struct lregs),
- VMSTATE_UINT32(int_enb, struct lregs),
- VMSTATE_UINT32(wstat, struct lregs),
- VMSTATE_END_OF_LIST()
- }
-};
-
-static const VMStateDescription vmstate_exynos4210_mct_lt = {
- .name = "exynos4210.mct.lt",
- .version_id = 1,
- .minimum_version_id = 1,
- .minimum_version_id_old = 1,
- .fields = (VMStateField[]) {
- VMSTATE_INT32(id, Exynos4210MCTLT),
- VMSTATE_STRUCT(tick_timer, Exynos4210MCTLT, 0,
- vmstate_tick_timer,
- struct tick_timer),
- VMSTATE_PTIMER(ptimer_frc, Exynos4210MCTLT),
- VMSTATE_STRUCT(reg, Exynos4210MCTLT, 0,
- vmstate_lregs,
- struct lregs),
- VMSTATE_END_OF_LIST()
- }
-};
-
-static const VMStateDescription vmstate_gregs = {
- .name = "exynos4210.mct.lregs",
- .version_id = 1,
- .minimum_version_id = 1,
- .minimum_version_id_old = 1,
- .fields = (VMStateField[]) {
- VMSTATE_UINT64(cnt, struct gregs),
- VMSTATE_UINT32(cnt_wstat, struct gregs),
- VMSTATE_UINT32(tcon, struct gregs),
- VMSTATE_UINT32(int_cstat, struct gregs),
- VMSTATE_UINT32(int_enb, struct gregs),
- VMSTATE_UINT32(wstat, struct gregs),
- VMSTATE_UINT64_ARRAY(comp, struct gregs, MCT_GT_CMP_NUM),
- VMSTATE_UINT32_ARRAY(comp_add_incr, struct gregs,
- MCT_GT_CMP_NUM),
- VMSTATE_END_OF_LIST()
- }
-};
-
-static const VMStateDescription vmstate_exynos4210_mct_gt = {
- .name = "exynos4210.mct.lt",
- .version_id = 1,
- .minimum_version_id = 1,
- .minimum_version_id_old = 1,
- .fields = (VMStateField[]) {
- VMSTATE_STRUCT(reg, Exynos4210MCTGT, 0, vmstate_gregs,
- struct gregs),
- VMSTATE_UINT64(count, Exynos4210MCTGT),
- VMSTATE_INT32(curr_comp, Exynos4210MCTGT),
- VMSTATE_PTIMER(ptimer_frc, Exynos4210MCTGT),
- VMSTATE_END_OF_LIST()
- }
-};
-
-static const VMStateDescription vmstate_exynos4210_mct_state = {
- .name = "exynos4210.mct",
- .version_id = 1,
- .minimum_version_id = 1,
- .minimum_version_id_old = 1,
- .fields = (VMStateField[]) {
- VMSTATE_UINT32(reg_mct_cfg, Exynos4210MCTState),
- VMSTATE_STRUCT_ARRAY(l_timer, Exynos4210MCTState, 2, 0,
- vmstate_exynos4210_mct_lt, Exynos4210MCTLT),
- VMSTATE_STRUCT(g_timer, Exynos4210MCTState, 0,
- vmstate_exynos4210_mct_gt, Exynos4210MCTGT),
- VMSTATE_UINT32(freq, Exynos4210MCTState),
- VMSTATE_END_OF_LIST()
- }
-};
-
-static void exynos4210_mct_update_freq(Exynos4210MCTState *s);
-
-/*
- * Set counter of FRC global timer.
- */
-static void exynos4210_gfrc_set_count(Exynos4210MCTGT *s, uint64_t count)
-{
- s->count = count;
- DPRINTF("global timer frc set count 0x%llx\n", count);
- ptimer_set_count(s->ptimer_frc, count);
-}
-
-/*
- * Get counter of FRC global timer.
- */
-static uint64_t exynos4210_gfrc_get_count(Exynos4210MCTGT *s)
-{
- uint64_t count = 0;
- count = ptimer_get_count(s->ptimer_frc);
- count = s->count - count;
- return s->reg.cnt + count;
-}
-
-/*
- * Stop global FRC timer
- */
-static void exynos4210_gfrc_stop(Exynos4210MCTGT *s)
-{
- DPRINTF("global timer frc stop\n");
-
- ptimer_stop(s->ptimer_frc);
-}
-
-/*
- * Start global FRC timer
- */
-static void exynos4210_gfrc_start(Exynos4210MCTGT *s)
-{
- DPRINTF("global timer frc start\n");
-
- ptimer_run(s->ptimer_frc, 1);
-}
-
-/*
- * Find next nearest Comparator. If current Comparator value equals to other
- * Comparator value, skip them both
- */
-static int32_t exynos4210_gcomp_find(Exynos4210MCTState *s)
-{
- int res;
- int i;
- int enabled;
- uint64_t min;
- int min_comp_i;
- uint64_t gfrc;
- uint64_t distance;
- uint64_t distance_min;
- int comp_i;
-
- /* get gfrc count */
- gfrc = exynos4210_gfrc_get_count(&s->g_timer);
-
- min = UINT64_MAX;
- distance_min = UINT64_MAX;
- comp_i = MCT_GT_CMP_NUM;
- min_comp_i = MCT_GT_CMP_NUM;
- enabled = 0;
-
- /* lookup for nearest comparator */
- for (i = 0; i < MCT_GT_CMP_NUM; i++) {
-
- if (s->g_timer.reg.tcon & G_TCON_COMP_ENABLE(i)) {
-
- enabled = 1;
-
- if (s->g_timer.reg.comp[i] > gfrc) {
- /* Comparator is upper then FRC */
- distance = s->g_timer.reg.comp[i] - gfrc;
-
- if (distance <= distance_min) {
- distance_min = distance;
- comp_i = i;
- }
- } else {
- /* Comparator is below FRC, find the smallest */
-
- if (s->g_timer.reg.comp[i] <= min) {
- min = s->g_timer.reg.comp[i];
- min_comp_i = i;
- }
- }
- }
- }
-
- if (!enabled) {
- /* All Comparators disabled */
- res = -1;
- } else if (comp_i < MCT_GT_CMP_NUM) {
- /* Found upper Comparator */
- res = comp_i;
- } else {
- /* All Comparators are below or equal to FRC */
- res = min_comp_i;
- }
-
- DPRINTF("found comparator %d: comp 0x%llx distance 0x%llx, gfrc 0x%llx\n",
- res,
- s->g_timer.reg.comp[res],
- distance_min,
- gfrc);
-
- return res;
-}
-
-/*
- * Get distance to nearest Comparator
- */
-static uint64_t exynos4210_gcomp_get_distance(Exynos4210MCTState *s, int32_t id)
-{
- if (id == -1) {
- /* no enabled Comparators, choose max distance */
- return MCT_GT_COUNTER_STEP;
- }
- if (s->g_timer.reg.comp[id] - s->g_timer.reg.cnt < MCT_GT_COUNTER_STEP) {
- return s->g_timer.reg.comp[id] - s->g_timer.reg.cnt;
- } else {
- return MCT_GT_COUNTER_STEP;
- }
-}
-
-/*
- * Restart global FRC timer
- */
-static void exynos4210_gfrc_restart(Exynos4210MCTState *s)
-{
- uint64_t distance;
-
- exynos4210_gfrc_stop(&s->g_timer);
-
- s->g_timer.curr_comp = exynos4210_gcomp_find(s);
-
- distance = exynos4210_gcomp_get_distance(s, s->g_timer.curr_comp);
-
- if (distance > MCT_GT_COUNTER_STEP || !distance) {
- distance = MCT_GT_COUNTER_STEP;
- }
-
- exynos4210_gfrc_set_count(&s->g_timer, distance);
- exynos4210_gfrc_start(&s->g_timer);
-}
-
-/*
- * Raise global timer CMP IRQ
- */
-static void exynos4210_gcomp_raise_irq(void *opaque, uint32_t id)
-{
- Exynos4210MCTGT *s = opaque;
-
- /* If CSTAT is pending and IRQ is enabled */
- if ((s->reg.int_cstat & G_INT_CSTAT_COMP(id)) &&
- (s->reg.int_enb & G_INT_ENABLE(id))) {
- DPRINTF("gcmp timer[%d] IRQ\n", id);
- qemu_irq_raise(s->irq[id]);
- }
-}
-
-/*
- * Lower global timer CMP IRQ
- */
-static void exynos4210_gcomp_lower_irq(void *opaque, uint32_t id)
-{
- Exynos4210MCTGT *s = opaque;
- qemu_irq_lower(s->irq[id]);
-}
-
-/*
- * Global timer FRC event handler.
- * Each event occurs when internal counter reaches counter + MCT_GT_COUNTER_STEP
- * Every time we arm global FRC timer to count for MCT_GT_COUNTER_STEP value
- */
-static void exynos4210_gfrc_event(void *opaque)
-{
- Exynos4210MCTState *s = (Exynos4210MCTState *)opaque;
- int i;
- uint64_t distance;
-
- DPRINTF("\n");
-
- s->g_timer.reg.cnt += s->g_timer.count;
-
- /* Process all comparators */
- for (i = 0; i < MCT_GT_CMP_NUM; i++) {
-
- if (s->g_timer.reg.cnt == s->g_timer.reg.comp[i]) {
- /* reached nearest comparator */
-
- s->g_timer.reg.int_cstat |= G_INT_CSTAT_COMP(i);
-
- /* Auto increment */
- if (s->g_timer.reg.tcon & G_TCON_AUTO_ICREMENT(i)) {
- s->g_timer.reg.comp[i] += s->g_timer.reg.comp_add_incr[i];
- }
-
- /* IRQ */
- exynos4210_gcomp_raise_irq(&s->g_timer, i);
- }
- }
-
- /* Reload FRC to reach nearest comparator */
- s->g_timer.curr_comp = exynos4210_gcomp_find(s);
- distance = exynos4210_gcomp_get_distance(s, s->g_timer.curr_comp);
- if (distance > MCT_GT_COUNTER_STEP || !distance) {
- distance = MCT_GT_COUNTER_STEP;
- }
- exynos4210_gfrc_set_count(&s->g_timer, distance);
-
- exynos4210_gfrc_start(&s->g_timer);
-}
-
-/*
- * Get counter of FRC local timer.
- */
-static uint64_t exynos4210_lfrc_get_count(Exynos4210MCTLT *s)
-{
- return ptimer_get_count(s->ptimer_frc);
-}
-
-/*
- * Set counter of FRC local timer.
- */
-static void exynos4210_lfrc_update_count(Exynos4210MCTLT *s)
-{
- if (!s->reg.cnt[L_REG_CNT_FRCCNTB]) {
- ptimer_set_count(s->ptimer_frc, MCT_LT_COUNTER_STEP);
- } else {
- ptimer_set_count(s->ptimer_frc, s->reg.cnt[L_REG_CNT_FRCCNTB]);
- }
-}
-
-/*
- * Start local FRC timer
- */
-static void exynos4210_lfrc_start(Exynos4210MCTLT *s)
-{
- ptimer_run(s->ptimer_frc, 1);
-}
-
-/*
- * Stop local FRC timer
- */
-static void exynos4210_lfrc_stop(Exynos4210MCTLT *s)
-{
- ptimer_stop(s->ptimer_frc);
-}
-
-/*
- * Local timer free running counter tick handler
- */
-static void exynos4210_lfrc_event(void *opaque)
-{
- Exynos4210MCTLT * s = (Exynos4210MCTLT *)opaque;
-
- /* local frc expired */
-
- DPRINTF("\n");
-
- s->reg.int_cstat |= L_INT_CSTAT_FRCCNT;
-
- /* update frc counter */
- exynos4210_lfrc_update_count(s);
-
- /* raise irq */
- if (s->reg.int_enb & L_INT_INTENB_FRCEIE) {
- qemu_irq_raise(s->irq);
- }
-
- /* we reached here, this means that timer is enabled */
- exynos4210_lfrc_start(s);
-}
-
-static uint32_t exynos4210_ltick_int_get_cnto(struct tick_timer *s);
-static uint32_t exynos4210_ltick_cnt_get_cnto(struct tick_timer *s);
-static void exynos4210_ltick_recalc_count(struct tick_timer *s);
-
-/*
- * Action on enabling local tick int timer
- */
-static void exynos4210_ltick_int_start(struct tick_timer *s)
-{
- if (!s->int_run) {
- s->int_run = 1;
- }
-}
-
-/*
- * Action on disabling local tick int timer
- */
-static void exynos4210_ltick_int_stop(struct tick_timer *s)
-{
- if (s->int_run) {
- s->last_icnto = exynos4210_ltick_int_get_cnto(s);
- s->int_run = 0;
- }
-}
-
-/*
- * Get count for INT timer
- */
-static uint32_t exynos4210_ltick_int_get_cnto(struct tick_timer *s)
-{
- uint32_t icnto;
- uint64_t remain;
- uint64_t count;
- uint64_t counted;
- uint64_t cur_progress;
-
- count = ptimer_get_count(s->ptimer_tick);
- if (count) {
- /* timer is still counting, called not from event */
- counted = s->count - ptimer_get_count(s->ptimer_tick);
- cur_progress = s->progress + counted;
- } else {
- /* timer expired earlier */
- cur_progress = s->progress;
- }
-
- remain = s->distance - cur_progress;
-
- if (!s->int_run) {
- /* INT is stopped. */
- icnto = s->last_icnto;
- } else {
- /* Both are counting */
- icnto = remain / s->tcntb;
- }
-
- return icnto;
-}
-
-/*
- * Start local tick cnt timer.
- */
-static void exynos4210_ltick_cnt_start(struct tick_timer *s)
-{
- if (!s->cnt_run) {
-
- exynos4210_ltick_recalc_count(s);
- ptimer_set_count(s->ptimer_tick, s->count);
- ptimer_run(s->ptimer_tick, 1);
-
- s->cnt_run = 1;
- }
-}
-
-/*
- * Stop local tick cnt timer.
- */
-static void exynos4210_ltick_cnt_stop(struct tick_timer *s)
-{
- if (s->cnt_run) {
-
- s->last_tcnto = exynos4210_ltick_cnt_get_cnto(s);
-
- if (s->int_run) {
- exynos4210_ltick_int_stop(s);
- }
-
- ptimer_stop(s->ptimer_tick);
-
- s->cnt_run = 0;
- }
-}
-
-/*
- * Get counter for CNT timer
- */
-static uint32_t exynos4210_ltick_cnt_get_cnto(struct tick_timer *s)
-{
- uint32_t tcnto;
- uint32_t icnto;
- uint64_t remain;
- uint64_t counted;
- uint64_t count;
- uint64_t cur_progress;
-
- count = ptimer_get_count(s->ptimer_tick);
- if (count) {
- /* timer is still counting, called not from event */
- counted = s->count - ptimer_get_count(s->ptimer_tick);
- cur_progress = s->progress + counted;
- } else {
- /* timer expired earlier */
- cur_progress = s->progress;
- }
-
- remain = s->distance - cur_progress;
-
- if (!s->cnt_run) {
- /* Both are stopped. */
- tcnto = s->last_tcnto;
- } else if (!s->int_run) {
- /* INT counter is stopped, progress is by CNT timer */
- tcnto = remain % s->tcntb;
- } else {
- /* Both are counting */
- icnto = remain / s->tcntb;
- if (icnto) {
- tcnto = remain % (icnto * s->tcntb);
- } else {
- tcnto = remain % s->tcntb;
- }
- }
-
- return tcnto;
-}
-
-/*
- * Set new values of counters for CNT and INT timers
- */
-static void exynos4210_ltick_set_cntb(struct tick_timer *s, uint32_t new_cnt,
- uint32_t new_int)
-{
- uint32_t cnt_stopped = 0;
- uint32_t int_stopped = 0;
-
- if (s->cnt_run) {
- exynos4210_ltick_cnt_stop(s);
- cnt_stopped = 1;
- }
-
- if (s->int_run) {
- exynos4210_ltick_int_stop(s);
- int_stopped = 1;
- }
-
- s->tcntb = new_cnt + 1;
- s->icntb = new_int + 1;
-
- if (cnt_stopped) {
- exynos4210_ltick_cnt_start(s);
- }
- if (int_stopped) {
- exynos4210_ltick_int_start(s);
- }
-
-}
-
-/*
- * Calculate new counter value for tick timer
- */
-static void exynos4210_ltick_recalc_count(struct tick_timer *s)
-{
- uint64_t to_count;
-
- if ((s->cnt_run && s->last_tcnto) || (s->int_run && s->last_icnto)) {
- /*
- * one or both timers run and not counted to the end;
- * distance is not passed, recalculate with last_tcnto * last_icnto
- */
-
- if (s->last_tcnto) {
- to_count = s->last_tcnto * s->last_icnto;
- } else {
- to_count = s->last_icnto;
- }
- } else {
- /* distance is passed, recalculate with tcnto * icnto */
- if (s->icntb) {
- s->distance = s->tcntb * s->icntb;
- } else {
- s->distance = s->tcntb;
- }
-
- to_count = s->distance;
- s->progress = 0;
- }
-
- if (to_count > MCT_LT_COUNTER_STEP) {
- /* count by step */
- s->count = MCT_LT_COUNTER_STEP;
- } else {
- s->count = to_count;
- }
-}
-
-/*
- * Initialize tick_timer
- */
-static void exynos4210_ltick_timer_init(struct tick_timer *s)
-{
- exynos4210_ltick_int_stop(s);
- exynos4210_ltick_cnt_stop(s);
-
- s->count = 0;
- s->distance = 0;
- s->progress = 0;
- s->icntb = 0;
- s->tcntb = 0;
-}
-
-/*
- * tick_timer event.
- * Raises when abstract tick_timer expires.
- */
-static void exynos4210_ltick_timer_event(struct tick_timer *s)
-{
- s->progress += s->count;
-}
-
-/*
- * Local timer tick counter handler.
- * Don't use reloaded timers. If timer counter = zero
- * then handler called but after handler finished no
- * timer reload occurs.
- */
-static void exynos4210_ltick_event(void *opaque)
-{
- Exynos4210MCTLT * s = (Exynos4210MCTLT *)opaque;
- uint32_t tcnto;
- uint32_t icnto;
-#ifdef DEBUG_MCT
- static uint64_t time1[2] = {0};
- static uint64_t time2[2] = {0};
-#endif
-
- /* Call tick_timer event handler, it will update its tcntb and icntb. */
- exynos4210_ltick_timer_event(&s->tick_timer);
-
- /* get tick_timer cnt */
- tcnto = exynos4210_ltick_cnt_get_cnto(&s->tick_timer);
-
- /* get tick_timer int */
- icnto = exynos4210_ltick_int_get_cnto(&s->tick_timer);
-
- /* raise IRQ if needed */
- if (!icnto && s->reg.tcon & L_TCON_INT_START) {
- /* INT counter enabled and expired */
-
- s->reg.int_cstat |= L_INT_CSTAT_INTCNT;
-
- /* raise interrupt if enabled */
- if (s->reg.int_enb & L_INT_INTENB_ICNTEIE) {
-#ifdef DEBUG_MCT
- time2[s->id] = qemu_get_clock_ns(vm_clock);
- DPRINTF("local timer[%d] IRQ: %llx\n", s->id,
- time2[s->id] - time1[s->id]);
- time1[s->id] = time2[s->id];
-#endif
- qemu_irq_raise(s->irq);
- }
-
- /* reload ICNTB */
- if (s->reg.tcon & L_TCON_INTERVAL_MODE) {
- exynos4210_ltick_set_cntb(&s->tick_timer,
- s->reg.cnt[L_REG_CNT_TCNTB],
- s->reg.cnt[L_REG_CNT_ICNTB]);
- }
- } else {
- /* reload TCNTB */
- if (!tcnto) {
- exynos4210_ltick_set_cntb(&s->tick_timer,
- s->reg.cnt[L_REG_CNT_TCNTB],
- icnto);
- }
- }
-
- /* start tick_timer cnt */
- exynos4210_ltick_cnt_start(&s->tick_timer);
-
- /* start tick_timer int */
- exynos4210_ltick_int_start(&s->tick_timer);
-}
-
-/* update timer frequency */
-static void exynos4210_mct_update_freq(Exynos4210MCTState *s)
-{
- uint32_t freq = s->freq;
- s->freq = 24000000 /
- ((MCT_CFG_GET_PRESCALER(s->reg_mct_cfg)+1) *
- MCT_CFG_GET_DIVIDER(s->reg_mct_cfg));
-
- if (freq != s->freq) {
- DPRINTF("freq=%dHz\n", s->freq);
-
- /* global timer */
- ptimer_set_freq(s->g_timer.ptimer_frc, s->freq);
-
- /* local timer */
- ptimer_set_freq(s->l_timer[0].tick_timer.ptimer_tick, s->freq);
- ptimer_set_freq(s->l_timer[0].ptimer_frc, s->freq);
- ptimer_set_freq(s->l_timer[1].tick_timer.ptimer_tick, s->freq);
- ptimer_set_freq(s->l_timer[1].ptimer_frc, s->freq);
- }
-}
-
-/* set defaul_timer values for all fields */
-static void exynos4210_mct_reset(DeviceState *d)
-{
- Exynos4210MCTState *s = (Exynos4210MCTState *)d;
- uint32_t i;
-
- s->reg_mct_cfg = 0;
-
- /* global timer */
- memset(&s->g_timer.reg, 0, sizeof(s->g_timer.reg));
- exynos4210_gfrc_stop(&s->g_timer);
-
- /* local timer */
- memset(s->l_timer[0].reg.cnt, 0, sizeof(s->l_timer[0].reg.cnt));
- memset(s->l_timer[1].reg.cnt, 0, sizeof(s->l_timer[1].reg.cnt));
- for (i = 0; i < 2; i++) {
- s->l_timer[i].reg.int_cstat = 0;
- s->l_timer[i].reg.int_enb = 0;
- s->l_timer[i].reg.tcon = 0;
- s->l_timer[i].reg.wstat = 0;
- s->l_timer[i].tick_timer.count = 0;
- s->l_timer[i].tick_timer.distance = 0;
- s->l_timer[i].tick_timer.progress = 0;
- ptimer_stop(s->l_timer[i].ptimer_frc);
-
- exynos4210_ltick_timer_init(&s->l_timer[i].tick_timer);
- }
-
- exynos4210_mct_update_freq(s);
-
-}
-
-/* Multi Core Timer read */
-static uint64_t exynos4210_mct_read(void *opaque, hwaddr offset,
- unsigned size)
-{
- Exynos4210MCTState *s = (Exynos4210MCTState *)opaque;
- int index;
- int shift;
- uint64_t count;
- uint32_t value;
- int lt_i;
-
- switch (offset) {
-
- case MCT_CFG:
- value = s->reg_mct_cfg;
- break;
-
- case G_CNT_L: case G_CNT_U:
- shift = 8 * (offset & 0x4);
- count = exynos4210_gfrc_get_count(&s->g_timer);
- value = UINT32_MAX & (count >> shift);
- DPRINTF("read FRC=0x%llx\n", count);
- break;
-
- case G_CNT_WSTAT:
- value = s->g_timer.reg.cnt_wstat;
- break;
-
- case G_COMP_L(0): case G_COMP_L(1): case G_COMP_L(2): case G_COMP_L(3):
- case G_COMP_U(0): case G_COMP_U(1): case G_COMP_U(2): case G_COMP_U(3):
- index = GET_G_COMP_IDX(offset);
- shift = 8 * (offset & 0x4);
- value = UINT32_MAX & (s->g_timer.reg.comp[index] >> shift);
- break;
-
- case G_TCON:
- value = s->g_timer.reg.tcon;
- break;
-
- case G_INT_CSTAT:
- value = s->g_timer.reg.int_cstat;
- break;
-
- case G_INT_ENB:
- value = s->g_timer.reg.int_enb;
- break;
- break;
- case G_WSTAT:
- value = s->g_timer.reg.wstat;
- break;
-
- case G_COMP0_ADD_INCR: case G_COMP1_ADD_INCR:
- case G_COMP2_ADD_INCR: case G_COMP3_ADD_INCR:
- value = s->g_timer.reg.comp_add_incr[GET_G_COMP_ADD_INCR_IDX(offset)];
- break;
-
- /* Local timers */
- case L0_TCNTB: case L0_ICNTB: case L0_FRCNTB:
- case L1_TCNTB: case L1_ICNTB: case L1_FRCNTB:
- lt_i = GET_L_TIMER_IDX(offset);
- index = GET_L_TIMER_CNT_REG_IDX(offset, lt_i);
- value = s->l_timer[lt_i].reg.cnt[index];
- break;
-
- case L0_TCNTO: case L1_TCNTO:
- lt_i = GET_L_TIMER_IDX(offset);
-
- value = exynos4210_ltick_cnt_get_cnto(&s->l_timer[lt_i].tick_timer);
- DPRINTF("local timer[%d] read TCNTO %x\n", lt_i, value);
- break;
-
- case L0_ICNTO: case L1_ICNTO:
- lt_i = GET_L_TIMER_IDX(offset);
-
- value = exynos4210_ltick_int_get_cnto(&s->l_timer[lt_i].tick_timer);
- DPRINTF("local timer[%d] read ICNTO %x\n", lt_i, value);
- break;
-
- case L0_FRCNTO: case L1_FRCNTO:
- lt_i = GET_L_TIMER_IDX(offset);
-
- value = exynos4210_lfrc_get_count(&s->l_timer[lt_i]);
-
- break;
-
- case L0_TCON: case L1_TCON:
- lt_i = ((offset & 0xF00) - L0_TCNTB) / 0x100;
- value = s->l_timer[lt_i].reg.tcon;
- break;
-
- case L0_INT_CSTAT: case L1_INT_CSTAT:
- lt_i = ((offset & 0xF00) - L0_TCNTB) / 0x100;
- value = s->l_timer[lt_i].reg.int_cstat;
- break;
-
- case L0_INT_ENB: case L1_INT_ENB:
- lt_i = ((offset & 0xF00) - L0_TCNTB) / 0x100;
- value = s->l_timer[lt_i].reg.int_enb;
- break;
-
- case L0_WSTAT: case L1_WSTAT:
- lt_i = ((offset & 0xF00) - L0_TCNTB) / 0x100;
- value = s->l_timer[lt_i].reg.wstat;
- break;
-
- default:
- hw_error("exynos4210.mct: bad read offset "
- TARGET_FMT_plx "\n", offset);
- break;
- }
- return value;
-}
-
-/* MCT write */
-static void exynos4210_mct_write(void *opaque, hwaddr offset,
- uint64_t value, unsigned size)
-{
- Exynos4210MCTState *s = (Exynos4210MCTState *)opaque;
- int index; /* index in buffer which represents register set */
- int shift;
- int lt_i;
- uint64_t new_frc;
- uint32_t i;
- uint32_t old_val;
-#ifdef DEBUG_MCT
- static uint32_t icntb_max[2] = {0};
- static uint32_t icntb_min[2] = {UINT32_MAX, UINT32_MAX};
- static uint32_t tcntb_max[2] = {0};
- static uint32_t tcntb_min[2] = {UINT32_MAX, UINT32_MAX};
-#endif
-
- new_frc = s->g_timer.reg.cnt;
-
- switch (offset) {
-
- case MCT_CFG:
- s->reg_mct_cfg = value;
- exynos4210_mct_update_freq(s);
- break;
-
- case G_CNT_L:
- case G_CNT_U:
- if (offset == G_CNT_L) {
-
- DPRINTF("global timer write to reg.cntl %llx\n", value);
-
- new_frc = (s->g_timer.reg.cnt & (uint64_t)UINT32_MAX << 32) + value;
- s->g_timer.reg.cnt_wstat |= G_CNT_WSTAT_L;
- }
- if (offset == G_CNT_U) {
-
- DPRINTF("global timer write to reg.cntu %llx\n", value);
-
- new_frc = (s->g_timer.reg.cnt & UINT32_MAX) +
- ((uint64_t)value << 32);
- s->g_timer.reg.cnt_wstat |= G_CNT_WSTAT_U;
- }
-
- s->g_timer.reg.cnt = new_frc;
- exynos4210_gfrc_restart(s);
- break;
-
- case G_CNT_WSTAT:
- s->g_timer.reg.cnt_wstat &= ~(value);
- break;
-
- case G_COMP_L(0): case G_COMP_L(1): case G_COMP_L(2): case G_COMP_L(3):
- case G_COMP_U(0): case G_COMP_U(1): case G_COMP_U(2): case G_COMP_U(3):
- index = GET_G_COMP_IDX(offset);
- shift = 8 * (offset & 0x4);
- s->g_timer.reg.comp[index] =
- (s->g_timer.reg.comp[index] &
- (((uint64_t)UINT32_MAX << 32) >> shift)) +
- (value << shift);
-
- DPRINTF("comparator %d write 0x%llx val << %d\n", index, value, shift);
-
- if (offset&0x4) {
- s->g_timer.reg.wstat |= G_WSTAT_COMP_U(index);
- } else {
- s->g_timer.reg.wstat |= G_WSTAT_COMP_L(index);
- }
-
- exynos4210_gfrc_restart(s);
- break;
-
- case G_TCON:
- old_val = s->g_timer.reg.tcon;
- s->g_timer.reg.tcon = value;
- s->g_timer.reg.wstat |= G_WSTAT_TCON_WRITE;
-
- DPRINTF("global timer write to reg.g_tcon %llx\n", value);
-
- /* Start FRC if transition from disabled to enabled */
- if ((value & G_TCON_TIMER_ENABLE) > (old_val &
- G_TCON_TIMER_ENABLE)) {
- exynos4210_gfrc_start(&s->g_timer);
- }
- if ((value & G_TCON_TIMER_ENABLE) < (old_val &
- G_TCON_TIMER_ENABLE)) {
- exynos4210_gfrc_stop(&s->g_timer);
- }
-
- /* Start CMP if transition from disabled to enabled */
- for (i = 0; i < MCT_GT_CMP_NUM; i++) {
- if ((value & G_TCON_COMP_ENABLE(i)) != (old_val &
- G_TCON_COMP_ENABLE(i))) {
- exynos4210_gfrc_restart(s);
- }
- }
- break;
-
- case G_INT_CSTAT:
- s->g_timer.reg.int_cstat &= ~(value);
- for (i = 0; i < MCT_GT_CMP_NUM; i++) {
- if (value & G_INT_CSTAT_COMP(i)) {
- exynos4210_gcomp_lower_irq(&s->g_timer, i);
- }
- }
- break;
-
- case G_INT_ENB:
-
- /* Raise IRQ if transition from disabled to enabled and CSTAT pending */
- for (i = 0; i < MCT_GT_CMP_NUM; i++) {
- if ((value & G_INT_ENABLE(i)) > (s->g_timer.reg.tcon &
- G_INT_ENABLE(i))) {
- if (s->g_timer.reg.int_cstat & G_INT_CSTAT_COMP(i)) {
- exynos4210_gcomp_raise_irq(&s->g_timer, i);
- }
- }
-
- if ((value & G_INT_ENABLE(i)) < (s->g_timer.reg.tcon &
- G_INT_ENABLE(i))) {
- exynos4210_gcomp_lower_irq(&s->g_timer, i);
- }
- }
-
- DPRINTF("global timer INT enable %llx\n", value);
- s->g_timer.reg.int_enb = value;
- break;
-
- case G_WSTAT:
- s->g_timer.reg.wstat &= ~(value);
- break;
-
- case G_COMP0_ADD_INCR: case G_COMP1_ADD_INCR:
- case G_COMP2_ADD_INCR: case G_COMP3_ADD_INCR:
- index = GET_G_COMP_ADD_INCR_IDX(offset);
- s->g_timer.reg.comp_add_incr[index] = value;
- s->g_timer.reg.wstat |= G_WSTAT_COMP_ADDINCR(index);
- break;
-
- /* Local timers */
- case L0_TCON: case L1_TCON:
- lt_i = GET_L_TIMER_IDX(offset);
- old_val = s->l_timer[lt_i].reg.tcon;
-
- s->l_timer[lt_i].reg.wstat |= L_WSTAT_TCON_WRITE;
- s->l_timer[lt_i].reg.tcon = value;
-
- /* Stop local CNT */
- if ((value & L_TCON_TICK_START) <
- (old_val & L_TCON_TICK_START)) {
- DPRINTF("local timer[%d] stop cnt\n", lt_i);
- exynos4210_ltick_cnt_stop(&s->l_timer[lt_i].tick_timer);
- }
-
- /* Stop local INT */
- if ((value & L_TCON_INT_START) <
- (old_val & L_TCON_INT_START)) {
- DPRINTF("local timer[%d] stop int\n", lt_i);
- exynos4210_ltick_int_stop(&s->l_timer[lt_i].tick_timer);
- }
-
- /* Start local CNT */
- if ((value & L_TCON_TICK_START) >
- (old_val & L_TCON_TICK_START)) {
- DPRINTF("local timer[%d] start cnt\n", lt_i);
- exynos4210_ltick_cnt_start(&s->l_timer[lt_i].tick_timer);
- }
-
- /* Start local INT */
- if ((value & L_TCON_INT_START) >
- (old_val & L_TCON_INT_START)) {
- DPRINTF("local timer[%d] start int\n", lt_i);
- exynos4210_ltick_int_start(&s->l_timer[lt_i].tick_timer);
- }
-
- /* Start or Stop local FRC if TCON changed */
- if ((value & L_TCON_FRC_START) >
- (s->l_timer[lt_i].reg.tcon & L_TCON_FRC_START)) {
- DPRINTF("local timer[%d] start frc\n", lt_i);
- exynos4210_lfrc_start(&s->l_timer[lt_i]);
- }
- if ((value & L_TCON_FRC_START) <
- (s->l_timer[lt_i].reg.tcon & L_TCON_FRC_START)) {
- DPRINTF("local timer[%d] stop frc\n", lt_i);
- exynos4210_lfrc_stop(&s->l_timer[lt_i]);
- }
- break;
-
- case L0_TCNTB: case L1_TCNTB:
-
- lt_i = GET_L_TIMER_IDX(offset);
- index = GET_L_TIMER_CNT_REG_IDX(offset, lt_i);
-
- /*
- * TCNTB is updated to internal register only after CNT expired.
- * Due to this we should reload timer to nearest moment when CNT is
- * expired and then in event handler update tcntb to new TCNTB value.
- */
- exynos4210_ltick_set_cntb(&s->l_timer[lt_i].tick_timer, value,
- s->l_timer[lt_i].tick_timer.icntb);
-
- s->l_timer[lt_i].reg.wstat |= L_WSTAT_TCNTB_WRITE;
- s->l_timer[lt_i].reg.cnt[L_REG_CNT_TCNTB] = value;
-
-#ifdef DEBUG_MCT
- if (tcntb_min[lt_i] > value) {
- tcntb_min[lt_i] = value;
- }
- if (tcntb_max[lt_i] < value) {
- tcntb_max[lt_i] = value;
- }
- DPRINTF("local timer[%d] TCNTB write %llx; max=%x, min=%x\n",
- lt_i, value, tcntb_max[lt_i], tcntb_min[lt_i]);
-#endif
- break;
-
- case L0_ICNTB: case L1_ICNTB:
-
- lt_i = GET_L_TIMER_IDX(offset);
- index = GET_L_TIMER_CNT_REG_IDX(offset, lt_i);
-
- s->l_timer[lt_i].reg.wstat |= L_WSTAT_ICNTB_WRITE;
- s->l_timer[lt_i].reg.cnt[L_REG_CNT_ICNTB] = value &
- ~L_ICNTB_MANUAL_UPDATE;
-
- /*
- * We need to avoid too small values for TCNTB*ICNTB. If not, IRQ event
- * could raise too fast disallowing QEMU to execute target code.
- */
- if (s->l_timer[lt_i].reg.cnt[L_REG_CNT_ICNTB] *
- s->l_timer[lt_i].reg.cnt[L_REG_CNT_TCNTB] < MCT_LT_CNT_LOW_LIMIT) {
- if (!s->l_timer[lt_i].reg.cnt[L_REG_CNT_TCNTB]) {
- s->l_timer[lt_i].reg.cnt[L_REG_CNT_ICNTB] =
- MCT_LT_CNT_LOW_LIMIT;
- } else {
- s->l_timer[lt_i].reg.cnt[L_REG_CNT_ICNTB] =
- MCT_LT_CNT_LOW_LIMIT /
- s->l_timer[lt_i].reg.cnt[L_REG_CNT_TCNTB];
- }
- }
-
- if (value & L_ICNTB_MANUAL_UPDATE) {
- exynos4210_ltick_set_cntb(&s->l_timer[lt_i].tick_timer,
- s->l_timer[lt_i].tick_timer.tcntb,
- s->l_timer[lt_i].reg.cnt[L_REG_CNT_ICNTB]);
- }
-
-#ifdef DEBUG_MCT
- if (icntb_min[lt_i] > value) {
- icntb_min[lt_i] = value;
- }
- if (icntb_max[lt_i] < value) {
- icntb_max[lt_i] = value;
- }
-DPRINTF("local timer[%d] ICNTB write %llx; max=%x, min=%x\n\n",
- lt_i, value, icntb_max[lt_i], icntb_min[lt_i]);
-#endif
-break;
-
- case L0_FRCNTB: case L1_FRCNTB:
-
- lt_i = GET_L_TIMER_IDX(offset);
- index = GET_L_TIMER_CNT_REG_IDX(offset, lt_i);
-
- DPRINTF("local timer[%d] FRCNTB write %llx\n", lt_i, value);
-
- s->l_timer[lt_i].reg.wstat |= L_WSTAT_FRCCNTB_WRITE;
- s->l_timer[lt_i].reg.cnt[L_REG_CNT_FRCCNTB] = value;
-
- break;
-
- case L0_TCNTO: case L1_TCNTO:
- case L0_ICNTO: case L1_ICNTO:
- case L0_FRCNTO: case L1_FRCNTO:
- fprintf(stderr, "\n[exynos4210.mct: write to RO register "
- TARGET_FMT_plx "]\n\n", offset);
- break;
-
- case L0_INT_CSTAT: case L1_INT_CSTAT:
- lt_i = GET_L_TIMER_IDX(offset);
-
- DPRINTF("local timer[%d] CSTAT write %llx\n", lt_i, value);
-
- s->l_timer[lt_i].reg.int_cstat &= ~value;
- if (!s->l_timer[lt_i].reg.int_cstat) {
- qemu_irq_lower(s->l_timer[lt_i].irq);
- }
- break;
-
- case L0_INT_ENB: case L1_INT_ENB:
- lt_i = GET_L_TIMER_IDX(offset);
- old_val = s->l_timer[lt_i].reg.int_enb;
-
- /* Raise Local timer IRQ if cstat is pending */
- if ((value & L_INT_INTENB_ICNTEIE) > (old_val & L_INT_INTENB_ICNTEIE)) {
- if (s->l_timer[lt_i].reg.int_cstat & L_INT_CSTAT_INTCNT) {
- qemu_irq_raise(s->l_timer[lt_i].irq);
- }
- }
-
- s->l_timer[lt_i].reg.int_enb = value;
-
- break;
-
- case L0_WSTAT: case L1_WSTAT:
- lt_i = GET_L_TIMER_IDX(offset);
-
- s->l_timer[lt_i].reg.wstat &= ~value;
- break;
-
- default:
- hw_error("exynos4210.mct: bad write offset "
- TARGET_FMT_plx "\n", offset);
- break;
- }
-}
-
-static const MemoryRegionOps exynos4210_mct_ops = {
- .read = exynos4210_mct_read,
- .write = exynos4210_mct_write,
- .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-/* MCT init */
-static int exynos4210_mct_init(SysBusDevice *dev)
-{
- int i;
- Exynos4210MCTState *s = FROM_SYSBUS(Exynos4210MCTState, dev);
- QEMUBH *bh[2];
-
- /* Global timer */
- bh[0] = qemu_bh_new(exynos4210_gfrc_event, s);
- s->g_timer.ptimer_frc = ptimer_init(bh[0]);
- memset(&s->g_timer.reg, 0, sizeof(struct gregs));
-
- /* Local timers */
- for (i = 0; i < 2; i++) {
- bh[0] = qemu_bh_new(exynos4210_ltick_event, &s->l_timer[i]);
- bh[1] = qemu_bh_new(exynos4210_lfrc_event, &s->l_timer[i]);
- s->l_timer[i].tick_timer.ptimer_tick = ptimer_init(bh[0]);
- s->l_timer[i].ptimer_frc = ptimer_init(bh[1]);
- s->l_timer[i].id = i;
- }
-
- /* IRQs */
- for (i = 0; i < MCT_GT_CMP_NUM; i++) {
- sysbus_init_irq(dev, &s->g_timer.irq[i]);
- }
- for (i = 0; i < 2; i++) {
- sysbus_init_irq(dev, &s->l_timer[i].irq);
- }
-
- memory_region_init_io(&s->iomem, &exynos4210_mct_ops, s, "exynos4210-mct",
- MCT_SFR_SIZE);
- sysbus_init_mmio(dev, &s->iomem);
-
- return 0;
-}
-
-static void exynos4210_mct_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-
- k->init = exynos4210_mct_init;
- dc->reset = exynos4210_mct_reset;
- dc->vmsd = &vmstate_exynos4210_mct_state;
-}
-
-static const TypeInfo exynos4210_mct_info = {
- .name = "exynos4210.mct",
- .parent = TYPE_SYS_BUS_DEVICE,
- .instance_size = sizeof(Exynos4210MCTState),
- .class_init = exynos4210_mct_class_init,
-};
-
-static void exynos4210_mct_register_types(void)
-{
- type_register_static(&exynos4210_mct_info);
-}
-
-type_init(exynos4210_mct_register_types)
+++ /dev/null
-/*
- * Samsung exynos4210 Pulse Width Modulation Timer
- *
- * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd.
- * All rights reserved.
- *
- * Evgeny Voevodin <e.voevodin@samsung.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 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 <http://www.gnu.org/licenses/>.
- */
-
-#include "hw/sysbus.h"
-#include "qemu/timer.h"
-#include "qemu-common.h"
-#include "hw/ptimer.h"
-
-#include "hw/arm/exynos4210.h"
-
-//#define DEBUG_PWM
-
-#ifdef DEBUG_PWM
-#define DPRINTF(fmt, ...) \
- do { fprintf(stdout, "PWM: [%24s:%5d] " fmt, __func__, __LINE__, \
- ## __VA_ARGS__); } while (0)
-#else
-#define DPRINTF(fmt, ...) do {} while (0)
-#endif
-
-#define EXYNOS4210_PWM_TIMERS_NUM 5
-#define EXYNOS4210_PWM_REG_MEM_SIZE 0x50
-
-#define TCFG0 0x0000
-#define TCFG1 0x0004
-#define TCON 0x0008
-#define TCNTB0 0x000C
-#define TCMPB0 0x0010
-#define TCNTO0 0x0014
-#define TCNTB1 0x0018
-#define TCMPB1 0x001C
-#define TCNTO1 0x0020
-#define TCNTB2 0x0024
-#define TCMPB2 0x0028
-#define TCNTO2 0x002C
-#define TCNTB3 0x0030
-#define TCMPB3 0x0034
-#define TCNTO3 0x0038
-#define TCNTB4 0x003C
-#define TCNTO4 0x0040
-#define TINT_CSTAT 0x0044
-
-#define TCNTB(x) (0xC * (x))
-#define TCMPB(x) (0xC * (x) + 1)
-#define TCNTO(x) (0xC * (x) + 2)
-
-#define GET_PRESCALER(reg, x) (((reg) & (0xFF << (8 * (x)))) >> 8 * (x))
-#define GET_DIVIDER(reg, x) (1 << (((reg) & (0xF << (4 * (x)))) >> (4 * (x))))
-
-/*
- * Attention! Timer4 doesn't have OUTPUT_INVERTER,
- * so Auto Reload bit is not accessible by macros!
- */
-#define TCON_TIMER_BASE(x) (((x) ? 1 : 0) * 4 + 4 * (x))
-#define TCON_TIMER_START(x) (1 << (TCON_TIMER_BASE(x) + 0))
-#define TCON_TIMER_MANUAL_UPD(x) (1 << (TCON_TIMER_BASE(x) + 1))
-#define TCON_TIMER_OUTPUT_INV(x) (1 << (TCON_TIMER_BASE(x) + 2))
-#define TCON_TIMER_AUTO_RELOAD(x) (1 << (TCON_TIMER_BASE(x) + 3))
-#define TCON_TIMER4_AUTO_RELOAD (1 << 22)
-
-#define TINT_CSTAT_STATUS(x) (1 << (5 + (x)))
-#define TINT_CSTAT_ENABLE(x) (1 << (x))
-
-/* timer struct */
-typedef struct {
- uint32_t id; /* timer id */
- qemu_irq irq; /* local timer irq */
- uint32_t freq; /* timer frequency */
-
- /* use ptimer.c to represent count down timer */
- ptimer_state *ptimer; /* timer */
-
- /* registers */
- uint32_t reg_tcntb; /* counter register buffer */
- uint32_t reg_tcmpb; /* compare register buffer */
-
- struct Exynos4210PWMState *parent;
-
-} Exynos4210PWM;
-
-
-typedef struct Exynos4210PWMState {
- SysBusDevice busdev;
- MemoryRegion iomem;
-
- uint32_t reg_tcfg[2];
- uint32_t reg_tcon;
- uint32_t reg_tint_cstat;
-
- Exynos4210PWM timer[EXYNOS4210_PWM_TIMERS_NUM];
-
-} Exynos4210PWMState;
-
-/*** VMState ***/
-static const VMStateDescription vmstate_exynos4210_pwm = {
- .name = "exynos4210.pwm.pwm",
- .version_id = 1,
- .minimum_version_id = 1,
- .minimum_version_id_old = 1,
- .fields = (VMStateField[]) {
- VMSTATE_UINT32(id, Exynos4210PWM),
- VMSTATE_UINT32(freq, Exynos4210PWM),
- VMSTATE_PTIMER(ptimer, Exynos4210PWM),
- VMSTATE_UINT32(reg_tcntb, Exynos4210PWM),
- VMSTATE_UINT32(reg_tcmpb, Exynos4210PWM),
- VMSTATE_END_OF_LIST()
- }
-};
-
-static const VMStateDescription vmstate_exynos4210_pwm_state = {
- .name = "exynos4210.pwm",
- .version_id = 1,
- .minimum_version_id = 1,
- .minimum_version_id_old = 1,
- .fields = (VMStateField[]) {
- VMSTATE_UINT32_ARRAY(reg_tcfg, Exynos4210PWMState, 2),
- VMSTATE_UINT32(reg_tcon, Exynos4210PWMState),
- VMSTATE_UINT32(reg_tint_cstat, Exynos4210PWMState),
- VMSTATE_STRUCT_ARRAY(timer, Exynos4210PWMState,
- EXYNOS4210_PWM_TIMERS_NUM, 0,
- vmstate_exynos4210_pwm, Exynos4210PWM),
- VMSTATE_END_OF_LIST()
- }
-};
-
-/*
- * PWM update frequency
- */
-static void exynos4210_pwm_update_freq(Exynos4210PWMState *s, uint32_t id)
-{
- uint32_t freq;
- freq = s->timer[id].freq;
- if (id > 1) {
- s->timer[id].freq = 24000000 /
- ((GET_PRESCALER(s->reg_tcfg[0], 1) + 1) *
- (GET_DIVIDER(s->reg_tcfg[1], id)));
- } else {
- s->timer[id].freq = 24000000 /
- ((GET_PRESCALER(s->reg_tcfg[0], 0) + 1) *
- (GET_DIVIDER(s->reg_tcfg[1], id)));
- }
-
- if (freq != s->timer[id].freq) {
- ptimer_set_freq(s->timer[id].ptimer, s->timer[id].freq);
- DPRINTF("freq=%dHz\n", s->timer[id].freq);
- }
-}
-
-/*
- * Counter tick handler
- */
-static void exynos4210_pwm_tick(void *opaque)
-{
- Exynos4210PWM *s = (Exynos4210PWM *)opaque;
- Exynos4210PWMState *p = (Exynos4210PWMState *)s->parent;
- uint32_t id = s->id;
- bool cmp;
-
- DPRINTF("timer %d tick\n", id);
-
- /* set irq status */
- p->reg_tint_cstat |= TINT_CSTAT_STATUS(id);
-
- /* raise IRQ */
- if (p->reg_tint_cstat & TINT_CSTAT_ENABLE(id)) {
- DPRINTF("timer %d IRQ\n", id);
- qemu_irq_raise(p->timer[id].irq);
- }
-
- /* reload timer */
- if (id != 4) {
- cmp = p->reg_tcon & TCON_TIMER_AUTO_RELOAD(id);
- } else {
- cmp = p->reg_tcon & TCON_TIMER4_AUTO_RELOAD;
- }
-
- if (cmp) {
- DPRINTF("auto reload timer %d count to %x\n", id,
- p->timer[id].reg_tcntb);
- ptimer_set_count(p->timer[id].ptimer, p->timer[id].reg_tcntb);
- ptimer_run(p->timer[id].ptimer, 1);
- } else {
- /* stop timer, set status to STOP, see Basic Timer Operation */
- p->reg_tcon &= ~TCON_TIMER_START(id);
- ptimer_stop(p->timer[id].ptimer);
- }
-}
-
-/*
- * PWM Read
- */
-static uint64_t exynos4210_pwm_read(void *opaque, hwaddr offset,
- unsigned size)
-{
- Exynos4210PWMState *s = (Exynos4210PWMState *)opaque;
- uint32_t value = 0;
- int index;
-
- switch (offset) {
- case TCFG0: case TCFG1:
- index = (offset - TCFG0) >> 2;
- value = s->reg_tcfg[index];
- break;
-
- case TCON:
- value = s->reg_tcon;
- break;
-
- case TCNTB0: case TCNTB1:
- case TCNTB2: case TCNTB3: case TCNTB4:
- index = (offset - TCNTB0) / 0xC;
- value = s->timer[index].reg_tcntb;
- break;
-
- case TCMPB0: case TCMPB1:
- case TCMPB2: case TCMPB3:
- index = (offset - TCMPB0) / 0xC;
- value = s->timer[index].reg_tcmpb;
- break;
-
- case TCNTO0: case TCNTO1:
- case TCNTO2: case TCNTO3: case TCNTO4:
- index = (offset == TCNTO4) ? 4 : (offset - TCNTO0) / 0xC;
- value = ptimer_get_count(s->timer[index].ptimer);
- break;
-
- case TINT_CSTAT:
- value = s->reg_tint_cstat;
- break;
-
- default:
- fprintf(stderr,
- "[exynos4210.pwm: bad read offset " TARGET_FMT_plx "]\n",
- offset);
- break;
- }
- return value;
-}
-
-/*
- * PWM Write
- */
-static void exynos4210_pwm_write(void *opaque, hwaddr offset,
- uint64_t value, unsigned size)
-{
- Exynos4210PWMState *s = (Exynos4210PWMState *)opaque;
- int index;
- uint32_t new_val;
- int i;
-
- switch (offset) {
- case TCFG0: case TCFG1:
- index = (offset - TCFG0) >> 2;
- s->reg_tcfg[index] = value;
-
- /* update timers frequencies */
- for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) {
- exynos4210_pwm_update_freq(s, s->timer[i].id);
- }
- break;
-
- case TCON:
- for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) {
- if ((value & TCON_TIMER_MANUAL_UPD(i)) >
- (s->reg_tcon & TCON_TIMER_MANUAL_UPD(i))) {
- /*
- * TCNTB and TCMPB are loaded into TCNT and TCMP.
- * Update timers.
- */
-
- /* this will start timer to run, this ok, because
- * during processing start bit timer will be stopped
- * if needed */
- ptimer_set_count(s->timer[i].ptimer, s->timer[i].reg_tcntb);
- DPRINTF("set timer %d count to %x\n", i,
- s->timer[i].reg_tcntb);
- }
-
- if ((value & TCON_TIMER_START(i)) >
- (s->reg_tcon & TCON_TIMER_START(i))) {
- /* changed to start */
- ptimer_run(s->timer[i].ptimer, 1);
- DPRINTF("run timer %d\n", i);
- }
-
- if ((value & TCON_TIMER_START(i)) <
- (s->reg_tcon & TCON_TIMER_START(i))) {
- /* changed to stop */
- ptimer_stop(s->timer[i].ptimer);
- DPRINTF("stop timer %d\n", i);
- }
- }
- s->reg_tcon = value;
- break;
-
- case TCNTB0: case TCNTB1:
- case TCNTB2: case TCNTB3: case TCNTB4:
- index = (offset - TCNTB0) / 0xC;
- s->timer[index].reg_tcntb = value;
- break;
-
- case TCMPB0: case TCMPB1:
- case TCMPB2: case TCMPB3:
- index = (offset - TCMPB0) / 0xC;
- s->timer[index].reg_tcmpb = value;
- break;
-
- case TINT_CSTAT:
- new_val = (s->reg_tint_cstat & 0x3E0) + (0x1F & value);
- new_val &= ~(0x3E0 & value);
-
- for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) {
- if ((new_val & TINT_CSTAT_STATUS(i)) <
- (s->reg_tint_cstat & TINT_CSTAT_STATUS(i))) {
- qemu_irq_lower(s->timer[i].irq);
- }
- }
-
- s->reg_tint_cstat = new_val;
- break;
-
- default:
- fprintf(stderr,
- "[exynos4210.pwm: bad write offset " TARGET_FMT_plx "]\n",
- offset);
- break;
-
- }
-}
-
-/*
- * Set default values to timer fields and registers
- */
-static void exynos4210_pwm_reset(DeviceState *d)
-{
- Exynos4210PWMState *s = (Exynos4210PWMState *)d;
- int i;
- s->reg_tcfg[0] = 0x0101;
- s->reg_tcfg[1] = 0x0;
- s->reg_tcon = 0;
- s->reg_tint_cstat = 0;
- for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) {
- s->timer[i].reg_tcmpb = 0;
- s->timer[i].reg_tcntb = 0;
-
- exynos4210_pwm_update_freq(s, s->timer[i].id);
- ptimer_stop(s->timer[i].ptimer);
- }
-}
-
-static const MemoryRegionOps exynos4210_pwm_ops = {
- .read = exynos4210_pwm_read,
- .write = exynos4210_pwm_write,
- .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-/*
- * PWM timer initialization
- */
-static int exynos4210_pwm_init(SysBusDevice *dev)
-{
- Exynos4210PWMState *s = FROM_SYSBUS(Exynos4210PWMState, dev);
- int i;
- QEMUBH *bh;
-
- for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) {
- bh = qemu_bh_new(exynos4210_pwm_tick, &s->timer[i]);
- sysbus_init_irq(dev, &s->timer[i].irq);
- s->timer[i].ptimer = ptimer_init(bh);
- s->timer[i].id = i;
- s->timer[i].parent = s;
- }
-
- memory_region_init_io(&s->iomem, &exynos4210_pwm_ops, s, "exynos4210-pwm",
- EXYNOS4210_PWM_REG_MEM_SIZE);
- sysbus_init_mmio(dev, &s->iomem);
-
- return 0;
-}
-
-static void exynos4210_pwm_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-
- k->init = exynos4210_pwm_init;
- dc->reset = exynos4210_pwm_reset;
- dc->vmsd = &vmstate_exynos4210_pwm_state;
-}
-
-static const TypeInfo exynos4210_pwm_info = {
- .name = "exynos4210.pwm",
- .parent = TYPE_SYS_BUS_DEVICE,
- .instance_size = sizeof(Exynos4210PWMState),
- .class_init = exynos4210_pwm_class_init,
-};
-
-static void exynos4210_pwm_register_types(void)
-{
- type_register_static(&exynos4210_pwm_info);
-}
-
-type_init(exynos4210_pwm_register_types)
+++ /dev/null
-/*
- * Samsung exynos4210 Real Time Clock
- *
- * Copyright (c) 2012 Samsung Electronics Co., Ltd.
- * Ogurtsov Oleg <o.ogurtsov@samsung.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 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 <http://www.gnu.org/licenses/>.
- *
- */
-
-/* Description:
- * Register RTCCON:
- * CLKSEL Bit[1] not used
- * CLKOUTEN Bit[9] not used
- */
-
-#include "hw/sysbus.h"
-#include "qemu/timer.h"
-#include "qemu-common.h"
-#include "hw/ptimer.h"
-
-#include "hw/hw.h"
-#include "qemu/timer.h"
-#include "sysemu/sysemu.h"
-
-#include "hw/arm/exynos4210.h"
-
-#define DEBUG_RTC 0
-
-#if DEBUG_RTC
-#define DPRINTF(fmt, ...) \
- do { fprintf(stdout, "RTC: [%24s:%5d] " fmt, __func__, __LINE__, \
- ## __VA_ARGS__); } while (0)
-#else
-#define DPRINTF(fmt, ...) do {} while (0)
-#endif
-
-#define EXYNOS4210_RTC_REG_MEM_SIZE 0x0100
-
-#define INTP 0x0030
-#define RTCCON 0x0040
-#define TICCNT 0x0044
-#define RTCALM 0x0050
-#define ALMSEC 0x0054
-#define ALMMIN 0x0058
-#define ALMHOUR 0x005C
-#define ALMDAY 0x0060
-#define ALMMON 0x0064
-#define ALMYEAR 0x0068
-#define BCDSEC 0x0070
-#define BCDMIN 0x0074
-#define BCDHOUR 0x0078
-#define BCDDAY 0x007C
-#define BCDDAYWEEK 0x0080
-#define BCDMON 0x0084
-#define BCDYEAR 0x0088
-#define CURTICNT 0x0090
-
-#define TICK_TIMER_ENABLE 0x0100
-#define TICNT_THRESHHOLD 2
-
-
-#define RTC_ENABLE 0x0001
-
-#define INTP_TICK_ENABLE 0x0001
-#define INTP_ALM_ENABLE 0x0002
-
-#define ALARM_INT_ENABLE 0x0040
-
-#define RTC_BASE_FREQ 32768
-
-typedef struct Exynos4210RTCState {
- SysBusDevice busdev;
- MemoryRegion iomem;
-
- /* registers */
- uint32_t reg_intp;
- uint32_t reg_rtccon;
- uint32_t reg_ticcnt;
- uint32_t reg_rtcalm;
- uint32_t reg_almsec;
- uint32_t reg_almmin;
- uint32_t reg_almhour;
- uint32_t reg_almday;
- uint32_t reg_almmon;
- uint32_t reg_almyear;
- uint32_t reg_curticcnt;
-
- ptimer_state *ptimer; /* tick timer */
- ptimer_state *ptimer_1Hz; /* clock timer */
- uint32_t freq;
-
- qemu_irq tick_irq; /* Time Tick Generator irq */
- qemu_irq alm_irq; /* alarm irq */
-
- struct tm current_tm; /* current time */
-} Exynos4210RTCState;
-
-#define TICCKSEL(value) ((value & (0x0F << 4)) >> 4)
-
-/*** VMState ***/
-static const VMStateDescription vmstate_exynos4210_rtc_state = {
- .name = "exynos4210.rtc",
- .version_id = 1,
- .minimum_version_id = 1,
- .minimum_version_id_old = 1,
- .fields = (VMStateField[]) {
- VMSTATE_UINT32(reg_intp, Exynos4210RTCState),
- VMSTATE_UINT32(reg_rtccon, Exynos4210RTCState),
- VMSTATE_UINT32(reg_ticcnt, Exynos4210RTCState),
- VMSTATE_UINT32(reg_rtcalm, Exynos4210RTCState),
- VMSTATE_UINT32(reg_almsec, Exynos4210RTCState),
- VMSTATE_UINT32(reg_almmin, Exynos4210RTCState),
- VMSTATE_UINT32(reg_almhour, Exynos4210RTCState),
- VMSTATE_UINT32(reg_almday, Exynos4210RTCState),
- VMSTATE_UINT32(reg_almmon, Exynos4210RTCState),
- VMSTATE_UINT32(reg_almyear, Exynos4210RTCState),
- VMSTATE_UINT32(reg_curticcnt, Exynos4210RTCState),
- VMSTATE_PTIMER(ptimer, Exynos4210RTCState),
- VMSTATE_PTIMER(ptimer_1Hz, Exynos4210RTCState),
- VMSTATE_UINT32(freq, Exynos4210RTCState),
- VMSTATE_INT32(current_tm.tm_sec, Exynos4210RTCState),
- VMSTATE_INT32(current_tm.tm_min, Exynos4210RTCState),
- VMSTATE_INT32(current_tm.tm_hour, Exynos4210RTCState),
- VMSTATE_INT32(current_tm.tm_wday, Exynos4210RTCState),
- VMSTATE_INT32(current_tm.tm_mday, Exynos4210RTCState),
- VMSTATE_INT32(current_tm.tm_mon, Exynos4210RTCState),
- VMSTATE_INT32(current_tm.tm_year, Exynos4210RTCState),
- VMSTATE_END_OF_LIST()
- }
-};
-
-#define BCD3DIGITS(x) \
- ((uint32_t)to_bcd((uint8_t)(x % 100)) + \
- ((uint32_t)to_bcd((uint8_t)((x % 1000) / 100)) << 8))
-
-static void check_alarm_raise(Exynos4210RTCState *s)
-{
- unsigned int alarm_raise = 0;
- struct tm stm = s->current_tm;
-
- if ((s->reg_rtcalm & 0x01) &&
- (to_bcd((uint8_t)stm.tm_sec) == (uint8_t)s->reg_almsec)) {
- alarm_raise = 1;
- }
- if ((s->reg_rtcalm & 0x02) &&
- (to_bcd((uint8_t)stm.tm_min) == (uint8_t)s->reg_almmin)) {
- alarm_raise = 1;
- }
- if ((s->reg_rtcalm & 0x04) &&
- (to_bcd((uint8_t)stm.tm_hour) == (uint8_t)s->reg_almhour)) {
- alarm_raise = 1;
- }
- if ((s->reg_rtcalm & 0x08) &&
- (to_bcd((uint8_t)stm.tm_mday) == (uint8_t)s->reg_almday)) {
- alarm_raise = 1;
- }
- if ((s->reg_rtcalm & 0x10) &&
- (to_bcd((uint8_t)stm.tm_mon) == (uint8_t)s->reg_almmon)) {
- alarm_raise = 1;
- }
- if ((s->reg_rtcalm & 0x20) &&
- (BCD3DIGITS(stm.tm_year) == s->reg_almyear)) {
- alarm_raise = 1;
- }
-
- if (alarm_raise) {
- DPRINTF("ALARM IRQ\n");
- /* set irq status */
- s->reg_intp |= INTP_ALM_ENABLE;
- qemu_irq_raise(s->alm_irq);
- }
-}
-
-/*
- * RTC update frequency
- * Parameters:
- * reg_value - current RTCCON register or his new value
- */
-static void exynos4210_rtc_update_freq(Exynos4210RTCState *s,
- uint32_t reg_value)
-{
- uint32_t freq;
-
- freq = s->freq;
- /* set frequncy for time generator */
- s->freq = RTC_BASE_FREQ / (1 << TICCKSEL(reg_value));
-
- if (freq != s->freq) {
- ptimer_set_freq(s->ptimer, s->freq);
- DPRINTF("freq=%dHz\n", s->freq);
- }
-}
-
-/* month is between 0 and 11. */
-static int get_days_in_month(int month, int year)
-{
- static const int days_tab[12] = {
- 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
- };
- int d;
- if ((unsigned)month >= 12) {
- return 31;
- }
- d = days_tab[month];
- if (month == 1) {
- if ((year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0)) {
- d++;
- }
- }
- return d;
-}
-
-/* update 'tm' to the next second */
-static void rtc_next_second(struct tm *tm)
-{
- int days_in_month;
-
- tm->tm_sec++;
- if ((unsigned)tm->tm_sec >= 60) {
- tm->tm_sec = 0;
- tm->tm_min++;
- if ((unsigned)tm->tm_min >= 60) {
- tm->tm_min = 0;
- tm->tm_hour++;
- if ((unsigned)tm->tm_hour >= 24) {
- tm->tm_hour = 0;
- /* next day */
- tm->tm_wday++;
- if ((unsigned)tm->tm_wday >= 7) {
- tm->tm_wday = 0;
- }
- days_in_month = get_days_in_month(tm->tm_mon,
- tm->tm_year + 1900);
- tm->tm_mday++;
- if (tm->tm_mday < 1) {
- tm->tm_mday = 1;
- } else if (tm->tm_mday > days_in_month) {
- tm->tm_mday = 1;
- tm->tm_mon++;
- if (tm->tm_mon >= 12) {
- tm->tm_mon = 0;
- tm->tm_year++;
- }
- }
- }
- }
- }
-}
-
-/*
- * tick handler
- */
-static void exynos4210_rtc_tick(void *opaque)
-{
- Exynos4210RTCState *s = (Exynos4210RTCState *)opaque;
-
- DPRINTF("TICK IRQ\n");
- /* set irq status */
- s->reg_intp |= INTP_TICK_ENABLE;
- /* raise IRQ */
- qemu_irq_raise(s->tick_irq);
-
- /* restart timer */
- ptimer_set_count(s->ptimer, s->reg_ticcnt);
- ptimer_run(s->ptimer, 1);
-}
-
-/*
- * 1Hz clock handler
- */
-static void exynos4210_rtc_1Hz_tick(void *opaque)
-{
- Exynos4210RTCState *s = (Exynos4210RTCState *)opaque;
-
- rtc_next_second(&s->current_tm);
- /* DPRINTF("1Hz tick\n"); */
-
- /* raise IRQ */
- if (s->reg_rtcalm & ALARM_INT_ENABLE) {
- check_alarm_raise(s);
- }
-
- ptimer_set_count(s->ptimer_1Hz, RTC_BASE_FREQ);
- ptimer_run(s->ptimer_1Hz, 1);
-}
-
-/*
- * RTC Read
- */
-static uint64_t exynos4210_rtc_read(void *opaque, hwaddr offset,
- unsigned size)
-{
- uint32_t value = 0;
- Exynos4210RTCState *s = (Exynos4210RTCState *)opaque;
-
- switch (offset) {
- case INTP:
- value = s->reg_intp;
- break;
- case RTCCON:
- value = s->reg_rtccon;
- break;
- case TICCNT:
- value = s->reg_ticcnt;
- break;
- case RTCALM:
- value = s->reg_rtcalm;
- break;
- case ALMSEC:
- value = s->reg_almsec;
- break;
- case ALMMIN:
- value = s->reg_almmin;
- break;
- case ALMHOUR:
- value = s->reg_almhour;
- break;
- case ALMDAY:
- value = s->reg_almday;
- break;
- case ALMMON:
- value = s->reg_almmon;
- break;
- case ALMYEAR:
- value = s->reg_almyear;
- break;
-
- case BCDSEC:
- value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_sec);
- break;
- case BCDMIN:
- value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_min);
- break;
- case BCDHOUR:
- value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_hour);
- break;
- case BCDDAYWEEK:
- value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_wday);
- break;
- case BCDDAY:
- value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_mday);
- break;
- case BCDMON:
- value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_mon + 1);
- break;
- case BCDYEAR:
- value = BCD3DIGITS(s->current_tm.tm_year);
- break;
-
- case CURTICNT:
- s->reg_curticcnt = ptimer_get_count(s->ptimer);
- value = s->reg_curticcnt;
- break;
-
- default:
- fprintf(stderr,
- "[exynos4210.rtc: bad read offset " TARGET_FMT_plx "]\n",
- offset);
- break;
- }
- return value;
-}
-
-/*
- * RTC Write
- */
-static void exynos4210_rtc_write(void *opaque, hwaddr offset,
- uint64_t value, unsigned size)
-{
- Exynos4210RTCState *s = (Exynos4210RTCState *)opaque;
-
- switch (offset) {
- case INTP:
- if (value & INTP_ALM_ENABLE) {
- qemu_irq_lower(s->alm_irq);
- s->reg_intp &= (~INTP_ALM_ENABLE);
- }
- if (value & INTP_TICK_ENABLE) {
- qemu_irq_lower(s->tick_irq);
- s->reg_intp &= (~INTP_TICK_ENABLE);
- }
- break;
- case RTCCON:
- if (value & RTC_ENABLE) {
- exynos4210_rtc_update_freq(s, value);
- }
- if ((value & RTC_ENABLE) > (s->reg_rtccon & RTC_ENABLE)) {
- /* clock timer */
- ptimer_set_count(s->ptimer_1Hz, RTC_BASE_FREQ);
- ptimer_run(s->ptimer_1Hz, 1);
- DPRINTF("run clock timer\n");
- }
- if ((value & RTC_ENABLE) < (s->reg_rtccon & RTC_ENABLE)) {
- /* tick timer */
- ptimer_stop(s->ptimer);
- /* clock timer */
- ptimer_stop(s->ptimer_1Hz);
- DPRINTF("stop all timers\n");
- }
- if (value & RTC_ENABLE) {
- if ((value & TICK_TIMER_ENABLE) >
- (s->reg_rtccon & TICK_TIMER_ENABLE) &&
- (s->reg_ticcnt)) {
- ptimer_set_count(s->ptimer, s->reg_ticcnt);
- ptimer_run(s->ptimer, 1);
- DPRINTF("run tick timer\n");
- }
- if ((value & TICK_TIMER_ENABLE) <
- (s->reg_rtccon & TICK_TIMER_ENABLE)) {
- ptimer_stop(s->ptimer);
- }
- }
- s->reg_rtccon = value;
- break;
- case TICCNT:
- if (value > TICNT_THRESHHOLD) {
- s->reg_ticcnt = value;
- } else {
- fprintf(stderr,
- "[exynos4210.rtc: bad TICNT value %u ]\n",
- (uint32_t)value);
- }
- break;
-
- case RTCALM:
- s->reg_rtcalm = value;
- break;
- case ALMSEC:
- s->reg_almsec = (value & 0x7f);
- break;
- case ALMMIN:
- s->reg_almmin = (value & 0x7f);
- break;
- case ALMHOUR:
- s->reg_almhour = (value & 0x3f);
- break;
- case ALMDAY:
- s->reg_almday = (value & 0x3f);
- break;
- case ALMMON:
- s->reg_almmon = (value & 0x1f);
- break;
- case ALMYEAR:
- s->reg_almyear = (value & 0x0fff);
- break;
-
- case BCDSEC:
- if (s->reg_rtccon & RTC_ENABLE) {
- s->current_tm.tm_sec = (int)from_bcd((uint8_t)value);
- }
- break;
- case BCDMIN:
- if (s->reg_rtccon & RTC_ENABLE) {
- s->current_tm.tm_min = (int)from_bcd((uint8_t)value);
- }
- break;
- case BCDHOUR:
- if (s->reg_rtccon & RTC_ENABLE) {
- s->current_tm.tm_hour = (int)from_bcd((uint8_t)value);
- }
- break;
- case BCDDAYWEEK:
- if (s->reg_rtccon & RTC_ENABLE) {
- s->current_tm.tm_wday = (int)from_bcd((uint8_t)value);
- }
- break;
- case BCDDAY:
- if (s->reg_rtccon & RTC_ENABLE) {
- s->current_tm.tm_mday = (int)from_bcd((uint8_t)value);
- }
- break;
- case BCDMON:
- if (s->reg_rtccon & RTC_ENABLE) {
- s->current_tm.tm_mon = (int)from_bcd((uint8_t)value) - 1;
- }
- break;
- case BCDYEAR:
- if (s->reg_rtccon & RTC_ENABLE) {
- /* 3 digits */
- s->current_tm.tm_year = (int)from_bcd((uint8_t)value) +
- (int)from_bcd((uint8_t)((value >> 8) & 0x0f)) * 100;
- }
- break;
-
- default:
- fprintf(stderr,
- "[exynos4210.rtc: bad write offset " TARGET_FMT_plx "]\n",
- offset);
- break;
-
- }
-}
-
-/*
- * Set default values to timer fields and registers
- */
-static void exynos4210_rtc_reset(DeviceState *d)
-{
- Exynos4210RTCState *s = (Exynos4210RTCState *)d;
-
- qemu_get_timedate(&s->current_tm, 0);
-
- DPRINTF("Get time from host: %d-%d-%d %2d:%02d:%02d\n",
- s->current_tm.tm_year, s->current_tm.tm_mon, s->current_tm.tm_mday,
- s->current_tm.tm_hour, s->current_tm.tm_min, s->current_tm.tm_sec);
-
- s->reg_intp = 0;
- s->reg_rtccon = 0;
- s->reg_ticcnt = 0;
- s->reg_rtcalm = 0;
- s->reg_almsec = 0;
- s->reg_almmin = 0;
- s->reg_almhour = 0;
- s->reg_almday = 0;
- s->reg_almmon = 0;
- s->reg_almyear = 0;
-
- s->reg_curticcnt = 0;
-
- exynos4210_rtc_update_freq(s, s->reg_rtccon);
- ptimer_stop(s->ptimer);
- ptimer_stop(s->ptimer_1Hz);
-}
-
-static const MemoryRegionOps exynos4210_rtc_ops = {
- .read = exynos4210_rtc_read,
- .write = exynos4210_rtc_write,
- .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-/*
- * RTC timer initialization
- */
-static int exynos4210_rtc_init(SysBusDevice *dev)
-{
- Exynos4210RTCState *s = FROM_SYSBUS(Exynos4210RTCState, dev);
- QEMUBH *bh;
-
- bh = qemu_bh_new(exynos4210_rtc_tick, s);
- s->ptimer = ptimer_init(bh);
- ptimer_set_freq(s->ptimer, RTC_BASE_FREQ);
- exynos4210_rtc_update_freq(s, 0);
-
- bh = qemu_bh_new(exynos4210_rtc_1Hz_tick, s);
- s->ptimer_1Hz = ptimer_init(bh);
- ptimer_set_freq(s->ptimer_1Hz, RTC_BASE_FREQ);
-
- sysbus_init_irq(dev, &s->alm_irq);
- sysbus_init_irq(dev, &s->tick_irq);
-
- memory_region_init_io(&s->iomem, &exynos4210_rtc_ops, s, "exynos4210-rtc",
- EXYNOS4210_RTC_REG_MEM_SIZE);
- sysbus_init_mmio(dev, &s->iomem);
-
- return 0;
-}
-
-static void exynos4210_rtc_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-
- k->init = exynos4210_rtc_init;
- dc->reset = exynos4210_rtc_reset;
- dc->vmsd = &vmstate_exynos4210_rtc_state;
-}
-
-static const TypeInfo exynos4210_rtc_info = {
- .name = "exynos4210.rtc",
- .parent = TYPE_SYS_BUS_DEVICE,
- .instance_size = sizeof(Exynos4210RTCState),
- .class_init = exynos4210_rtc_class_init,
-};
-
-static void exynos4210_rtc_register_types(void)
-{
- type_register_static(&exynos4210_rtc_info);
-}
-
-type_init(exynos4210_rtc_register_types)
+++ /dev/null
-/*
- * QEMU GRLIB GPTimer Emulator
- *
- * Copyright (c) 2010-2011 AdaCore
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-#include "hw/sysbus.h"
-#include "qemu/timer.h"
-#include "hw/ptimer.h"
-
-#include "trace.h"
-
-#define UNIT_REG_SIZE 16 /* Size of memory mapped regs for the unit */
-#define GPTIMER_REG_SIZE 16 /* Size of memory mapped regs for a GPTimer */
-
-#define GPTIMER_MAX_TIMERS 8
-
-/* GPTimer Config register fields */
-#define GPTIMER_ENABLE (1 << 0)
-#define GPTIMER_RESTART (1 << 1)
-#define GPTIMER_LOAD (1 << 2)
-#define GPTIMER_INT_ENABLE (1 << 3)
-#define GPTIMER_INT_PENDING (1 << 4)
-#define GPTIMER_CHAIN (1 << 5) /* Not supported */
-#define GPTIMER_DEBUG_HALT (1 << 6) /* Not supported */
-
-/* Memory mapped register offsets */
-#define SCALER_OFFSET 0x00
-#define SCALER_RELOAD_OFFSET 0x04
-#define CONFIG_OFFSET 0x08
-#define COUNTER_OFFSET 0x00
-#define COUNTER_RELOAD_OFFSET 0x04
-#define TIMER_BASE 0x10
-
-typedef struct GPTimer GPTimer;
-typedef struct GPTimerUnit GPTimerUnit;
-
-struct GPTimer {
- QEMUBH *bh;
- struct ptimer_state *ptimer;
-
- qemu_irq irq;
- int id;
- GPTimerUnit *unit;
-
- /* registers */
- uint32_t counter;
- uint32_t reload;
- uint32_t config;
-};
-
-struct GPTimerUnit {
- SysBusDevice busdev;
- MemoryRegion iomem;
-
- uint32_t nr_timers; /* Number of timers available */
- uint32_t freq_hz; /* System frequency */
- uint32_t irq_line; /* Base irq line */
-
- GPTimer *timers;
-
- /* registers */
- uint32_t scaler;
- uint32_t reload;
- uint32_t config;
-};
-
-static void grlib_gptimer_enable(GPTimer *timer)
-{
- assert(timer != NULL);
-
-
- ptimer_stop(timer->ptimer);
-
- if (!(timer->config & GPTIMER_ENABLE)) {
- /* Timer disabled */
- trace_grlib_gptimer_disabled(timer->id, timer->config);
- return;
- }
-
- /* ptimer is triggered when the counter reach 0 but GPTimer is triggered at
- underflow. Set count + 1 to simulate the GPTimer behavior. */
-
- trace_grlib_gptimer_enable(timer->id, timer->counter + 1);
-
- ptimer_set_count(timer->ptimer, timer->counter + 1);
- ptimer_run(timer->ptimer, 1);
-}
-
-static void grlib_gptimer_restart(GPTimer *timer)
-{
- assert(timer != NULL);
-
- trace_grlib_gptimer_restart(timer->id, timer->reload);
-
- timer->counter = timer->reload;
- grlib_gptimer_enable(timer);
-}
-
-static void grlib_gptimer_set_scaler(GPTimerUnit *unit, uint32_t scaler)
-{
- int i = 0;
- uint32_t value = 0;
-
- assert(unit != NULL);
-
- if (scaler > 0) {
- value = unit->freq_hz / (scaler + 1);
- } else {
- value = unit->freq_hz;
- }
-
- trace_grlib_gptimer_set_scaler(scaler, value);
-
- for (i = 0; i < unit->nr_timers; i++) {
- ptimer_set_freq(unit->timers[i].ptimer, value);
- }
-}
-
-static void grlib_gptimer_hit(void *opaque)
-{
- GPTimer *timer = opaque;
- assert(timer != NULL);
-
- trace_grlib_gptimer_hit(timer->id);
-
- /* Timer expired */
-
- if (timer->config & GPTIMER_INT_ENABLE) {
- /* Set the pending bit (only unset by write in the config register) */
- timer->config |= GPTIMER_INT_PENDING;
- qemu_irq_pulse(timer->irq);
- }
-
- if (timer->config & GPTIMER_RESTART) {
- grlib_gptimer_restart(timer);
- }
-}
-
-static uint64_t grlib_gptimer_read(void *opaque, hwaddr addr,
- unsigned size)
-{
- GPTimerUnit *unit = opaque;
- hwaddr timer_addr;
- int id;
- uint32_t value = 0;
-
- addr &= 0xff;
-
- /* Unit registers */
- switch (addr) {
- case SCALER_OFFSET:
- trace_grlib_gptimer_readl(-1, addr, unit->scaler);
- return unit->scaler;
-
- case SCALER_RELOAD_OFFSET:
- trace_grlib_gptimer_readl(-1, addr, unit->reload);
- return unit->reload;
-
- case CONFIG_OFFSET:
- trace_grlib_gptimer_readl(-1, addr, unit->config);
- return unit->config;
-
- default:
- break;
- }
-
- timer_addr = (addr % TIMER_BASE);
- id = (addr - TIMER_BASE) / TIMER_BASE;
-
- if (id >= 0 && id < unit->nr_timers) {
-
- /* GPTimer registers */
- switch (timer_addr) {
- case COUNTER_OFFSET:
- value = ptimer_get_count(unit->timers[id].ptimer);
- trace_grlib_gptimer_readl(id, addr, value);
- return value;
-
- case COUNTER_RELOAD_OFFSET:
- value = unit->timers[id].reload;
- trace_grlib_gptimer_readl(id, addr, value);
- return value;
-
- case CONFIG_OFFSET:
- trace_grlib_gptimer_readl(id, addr, unit->timers[id].config);
- return unit->timers[id].config;
-
- default:
- break;
- }
-
- }
-
- trace_grlib_gptimer_readl(-1, addr, 0);
- return 0;
-}
-
-static void grlib_gptimer_write(void *opaque, hwaddr addr,
- uint64_t value, unsigned size)
-{
- GPTimerUnit *unit = opaque;
- hwaddr timer_addr;
- int id;
-
- addr &= 0xff;
-
- /* Unit registers */
- switch (addr) {
- case SCALER_OFFSET:
- value &= 0xFFFF; /* clean up the value */
- unit->scaler = value;
- trace_grlib_gptimer_writel(-1, addr, unit->scaler);
- return;
-
- case SCALER_RELOAD_OFFSET:
- value &= 0xFFFF; /* clean up the value */
- unit->reload = value;
- trace_grlib_gptimer_writel(-1, addr, unit->reload);
- grlib_gptimer_set_scaler(unit, value);
- return;
-
- case CONFIG_OFFSET:
- /* Read Only (disable timer freeze not supported) */
- trace_grlib_gptimer_writel(-1, addr, 0);
- return;
-
- default:
- break;
- }
-
- timer_addr = (addr % TIMER_BASE);
- id = (addr - TIMER_BASE) / TIMER_BASE;
-
- if (id >= 0 && id < unit->nr_timers) {
-
- /* GPTimer registers */
- switch (timer_addr) {
- case COUNTER_OFFSET:
- trace_grlib_gptimer_writel(id, addr, value);
- unit->timers[id].counter = value;
- grlib_gptimer_enable(&unit->timers[id]);
- return;
-
- case COUNTER_RELOAD_OFFSET:
- trace_grlib_gptimer_writel(id, addr, value);
- unit->timers[id].reload = value;
- return;
-
- case CONFIG_OFFSET:
- trace_grlib_gptimer_writel(id, addr, value);
-
- if (value & GPTIMER_INT_PENDING) {
- /* clear pending bit */
- value &= ~GPTIMER_INT_PENDING;
- } else {
- /* keep pending bit */
- value |= unit->timers[id].config & GPTIMER_INT_PENDING;
- }
-
- unit->timers[id].config = value;
-
- /* gptimer_restart calls gptimer_enable, so if "enable" and "load"
- bits are present, we just have to call restart. */
-
- if (value & GPTIMER_LOAD) {
- grlib_gptimer_restart(&unit->timers[id]);
- } else if (value & GPTIMER_ENABLE) {
- grlib_gptimer_enable(&unit->timers[id]);
- }
-
- /* These fields must always be read as 0 */
- value &= ~(GPTIMER_LOAD & GPTIMER_DEBUG_HALT);
-
- unit->timers[id].config = value;
- return;
-
- default:
- break;
- }
-
- }
-
- trace_grlib_gptimer_writel(-1, addr, value);
-}
-
-static const MemoryRegionOps grlib_gptimer_ops = {
- .read = grlib_gptimer_read,
- .write = grlib_gptimer_write,
- .endianness = DEVICE_NATIVE_ENDIAN,
- .valid = {
- .min_access_size = 4,
- .max_access_size = 4,
- },
-};
-
-static void grlib_gptimer_reset(DeviceState *d)
-{
- GPTimerUnit *unit = container_of(d, GPTimerUnit, busdev.qdev);
- int i = 0;
-
- assert(unit != NULL);
-
- unit->scaler = 0;
- unit->reload = 0;
- unit->config = 0;
-
- unit->config = unit->nr_timers;
- unit->config |= unit->irq_line << 3;
- unit->config |= 1 << 8; /* separate interrupt */
- unit->config |= 1 << 9; /* Disable timer freeze */
-
-
- for (i = 0; i < unit->nr_timers; i++) {
- GPTimer *timer = &unit->timers[i];
-
- timer->counter = 0;
- timer->reload = 0;
- timer->config = 0;
- ptimer_stop(timer->ptimer);
- ptimer_set_count(timer->ptimer, 0);
- ptimer_set_freq(timer->ptimer, unit->freq_hz);
- }
-}
-
-static int grlib_gptimer_init(SysBusDevice *dev)
-{
- GPTimerUnit *unit = FROM_SYSBUS(typeof(*unit), dev);
- unsigned int i;
-
- assert(unit->nr_timers > 0);
- assert(unit->nr_timers <= GPTIMER_MAX_TIMERS);
-
- unit->timers = g_malloc0(sizeof unit->timers[0] * unit->nr_timers);
-
- for (i = 0; i < unit->nr_timers; i++) {
- GPTimer *timer = &unit->timers[i];
-
- timer->unit = unit;
- timer->bh = qemu_bh_new(grlib_gptimer_hit, timer);
- timer->ptimer = ptimer_init(timer->bh);
- timer->id = i;
-
- /* One IRQ line for each timer */
- sysbus_init_irq(dev, &timer->irq);
-
- ptimer_set_freq(timer->ptimer, unit->freq_hz);
- }
-
- memory_region_init_io(&unit->iomem, &grlib_gptimer_ops, unit, "gptimer",
- UNIT_REG_SIZE + GPTIMER_REG_SIZE * unit->nr_timers);
-
- sysbus_init_mmio(dev, &unit->iomem);
- return 0;
-}
-
-static Property grlib_gptimer_properties[] = {
- DEFINE_PROP_UINT32("frequency", GPTimerUnit, freq_hz, 40000000),
- DEFINE_PROP_UINT32("irq-line", GPTimerUnit, irq_line, 8),
- DEFINE_PROP_UINT32("nr-timers", GPTimerUnit, nr_timers, 2),
- DEFINE_PROP_END_OF_LIST(),
-};
-
-static void grlib_gptimer_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-
- k->init = grlib_gptimer_init;
- dc->reset = grlib_gptimer_reset;
- dc->props = grlib_gptimer_properties;
-}
-
-static const TypeInfo grlib_gptimer_info = {
- .name = "grlib,gptimer",
- .parent = TYPE_SYS_BUS_DEVICE,
- .instance_size = sizeof(GPTimerUnit),
- .class_init = grlib_gptimer_class_init,
-};
-
-static void grlib_gptimer_register_types(void)
-{
- type_register_static(&grlib_gptimer_info);
-}
-
-type_init(grlib_gptimer_register_types)
+++ /dev/null
-/*
- * IMX31 Timer
- *
- * Copyright (c) 2008 OK Labs
- * Copyright (c) 2011 NICTA Pty Ltd
- * Originally written by Hans Jiang
- * Updated by Peter Chubb
- *
- * This code is licensed under GPL version 2 or later. See
- * the COPYING file in the top-level directory.
- *
- */
-
-#include "hw/hw.h"
-#include "qemu/timer.h"
-#include "hw/ptimer.h"
-#include "hw/sysbus.h"
-#include "hw/arm/imx.h"
-
-//#define DEBUG_TIMER 1
-#ifdef DEBUG_TIMER
-# define DPRINTF(fmt, args...) \
- do { printf("imx_timer: " fmt , ##args); } while (0)
-#else
-# define DPRINTF(fmt, args...) do {} while (0)
-#endif
-
-/*
- * Define to 1 for messages about attempts to
- * access unimplemented registers or similar.
- */
-#define DEBUG_IMPLEMENTATION 1
-#if DEBUG_IMPLEMENTATION
-# define IPRINTF(fmt, args...) \
- do { fprintf(stderr, "imx_timer: " fmt, ##args); } while (0)
-#else
-# define IPRINTF(fmt, args...) do {} while (0)
-#endif
-
-/*
- * GPT : General purpose timer
- *
- * This timer counts up continuously while it is enabled, resetting itself
- * to 0 when it reaches TIMER_MAX (in freerun mode) or when it
- * reaches the value of ocr1 (in periodic mode). WE simulate this using a
- * QEMU ptimer counting down from ocr1 and reloading from ocr1 in
- * periodic mode, or counting from ocr1 to zero, then TIMER_MAX - ocr1.
- * waiting_rov is set when counting from TIMER_MAX.
- *
- * In the real hardware, there are three comparison registers that can
- * trigger interrupts, and compare channel 1 can be used to
- * force-reset the timer. However, this is a `bare-bones'
- * implementation: only what Linux 3.x uses has been implemented
- * (free-running timer from 0 to OCR1 or TIMER_MAX) .
- */
-
-
-#define TIMER_MAX 0XFFFFFFFFUL
-
-/* Control register. Not all of these bits have any effect (yet) */
-#define GPT_CR_EN (1 << 0) /* GPT Enable */
-#define GPT_CR_ENMOD (1 << 1) /* GPT Enable Mode */
-#define GPT_CR_DBGEN (1 << 2) /* GPT Debug mode enable */
-#define GPT_CR_WAITEN (1 << 3) /* GPT Wait Mode Enable */
-#define GPT_CR_DOZEN (1 << 4) /* GPT Doze mode enable */
-#define GPT_CR_STOPEN (1 << 5) /* GPT Stop Mode Enable */
-#define GPT_CR_CLKSRC_SHIFT (6)
-#define GPT_CR_CLKSRC_MASK (0x7)
-
-#define GPT_CR_FRR (1 << 9) /* Freerun or Restart */
-#define GPT_CR_SWR (1 << 15) /* Software Reset */
-#define GPT_CR_IM1 (3 << 16) /* Input capture channel 1 mode (2 bits) */
-#define GPT_CR_IM2 (3 << 18) /* Input capture channel 2 mode (2 bits) */
-#define GPT_CR_OM1 (7 << 20) /* Output Compare Channel 1 Mode (3 bits) */
-#define GPT_CR_OM2 (7 << 23) /* Output Compare Channel 2 Mode (3 bits) */
-#define GPT_CR_OM3 (7 << 26) /* Output Compare Channel 3 Mode (3 bits) */
-#define GPT_CR_FO1 (1 << 29) /* Force Output Compare Channel 1 */
-#define GPT_CR_FO2 (1 << 30) /* Force Output Compare Channel 2 */
-#define GPT_CR_FO3 (1 << 31) /* Force Output Compare Channel 3 */
-
-#define GPT_SR_OF1 (1 << 0)
-#define GPT_SR_ROV (1 << 5)
-
-#define GPT_IR_OF1IE (1 << 0)
-#define GPT_IR_ROVIE (1 << 5)
-
-typedef struct {
- SysBusDevice busdev;
- ptimer_state *timer;
- MemoryRegion iomem;
- DeviceState *ccm;
-
- uint32_t cr;
- uint32_t pr;
- uint32_t sr;
- uint32_t ir;
- uint32_t ocr1;
- uint32_t cnt;
-
- uint32_t waiting_rov;
- qemu_irq irq;
-} IMXTimerGState;
-
-static const VMStateDescription vmstate_imx_timerg = {
- .name = "imx-timerg",
- .version_id = 1,
- .minimum_version_id = 1,
- .minimum_version_id_old = 1,
- .fields = (VMStateField[]) {
- VMSTATE_UINT32(cr, IMXTimerGState),
- VMSTATE_UINT32(pr, IMXTimerGState),
- VMSTATE_UINT32(sr, IMXTimerGState),
- VMSTATE_UINT32(ir, IMXTimerGState),
- VMSTATE_UINT32(ocr1, IMXTimerGState),
- VMSTATE_UINT32(cnt, IMXTimerGState),
- VMSTATE_UINT32(waiting_rov, IMXTimerGState),
- VMSTATE_PTIMER(timer, IMXTimerGState),
- VMSTATE_END_OF_LIST()
- }
-};
-
-static const IMXClk imx_timerg_clocks[] = {
- NOCLK, /* 000 No clock source */
- IPG, /* 001 ipg_clk, 532MHz*/
- IPG, /* 010 ipg_clk_highfreq */
- NOCLK, /* 011 not defined */
- CLK_32k, /* 100 ipg_clk_32k */
- NOCLK, /* 101 not defined */
- NOCLK, /* 110 not defined */
- NOCLK, /* 111 not defined */
-};
-
-
-static void imx_timerg_set_freq(IMXTimerGState *s)
-{
- int clksrc;
- uint32_t freq;
-
- clksrc = (s->cr >> GPT_CR_CLKSRC_SHIFT) & GPT_CR_CLKSRC_MASK;
- freq = imx_clock_frequency(s->ccm, imx_timerg_clocks[clksrc]) / (1 + s->pr);
-
- DPRINTF("Setting gtimer clksrc %d to frequency %d\n", clksrc, freq);
- if (freq) {
- ptimer_set_freq(s->timer, freq);
- }
-}
-
-static void imx_timerg_update(IMXTimerGState *s)
-{
- uint32_t flags = s->sr & s->ir & (GPT_SR_OF1 | GPT_SR_ROV);
-
- DPRINTF("g-timer SR: %s %s IR=%s %s, %s\n",
- s->sr & GPT_SR_OF1 ? "OF1" : "",
- s->sr & GPT_SR_ROV ? "ROV" : "",
- s->ir & GPT_SR_OF1 ? "OF1" : "",
- s->ir & GPT_SR_ROV ? "ROV" : "",
- s->cr & GPT_CR_EN ? "CR_EN" : "Not Enabled");
-
-
- qemu_set_irq(s->irq, (s->cr & GPT_CR_EN) && flags);
-}
-
-static uint32_t imx_timerg_update_counts(IMXTimerGState *s)
-{
- uint64_t target = s->waiting_rov ? TIMER_MAX : s->ocr1;
- uint64_t cnt = ptimer_get_count(s->timer);
- s->cnt = target - cnt;
- return s->cnt;
-}
-
-static void imx_timerg_reload(IMXTimerGState *s, uint32_t timeout)
-{
- uint64_t diff_cnt;
-
- if (!(s->cr & GPT_CR_FRR)) {
- IPRINTF("IMX_timerg_reload --- called in reset-mode\n");
- return;
- }
-
- /*
- * For small timeouts, qemu sometimes runs too slow.
- * Better deliver a late interrupt than none.
- *
- * In Reset mode (FRR bit clear)
- * the ptimer reloads itself from OCR1;
- * in free-running mode we need to fake
- * running from 0 to ocr1 to TIMER_MAX
- */
- if (timeout > s->cnt) {
- diff_cnt = timeout - s->cnt;
- } else {
- diff_cnt = 0;
- }
- ptimer_set_count(s->timer, diff_cnt);
-}
-
-static uint64_t imx_timerg_read(void *opaque, hwaddr offset,
- unsigned size)
-{
- IMXTimerGState *s = (IMXTimerGState *)opaque;
-
- DPRINTF("g-read(offset=%x)", offset >> 2);
- switch (offset >> 2) {
- case 0: /* Control Register */
- DPRINTF(" cr = %x\n", s->cr);
- return s->cr;
-
- case 1: /* prescaler */
- DPRINTF(" pr = %x\n", s->pr);
- return s->pr;
-
- case 2: /* Status Register */
- DPRINTF(" sr = %x\n", s->sr);
- return s->sr;
-
- case 3: /* Interrupt Register */
- DPRINTF(" ir = %x\n", s->ir);
- return s->ir;
-
- case 4: /* Output Compare Register 1 */
- DPRINTF(" ocr1 = %x\n", s->ocr1);
- return s->ocr1;
-
-
- case 9: /* cnt */
- imx_timerg_update_counts(s);
- DPRINTF(" cnt = %x\n", s->cnt);
- return s->cnt;
- }
-
- IPRINTF("imx_timerg_read: Bad offset %x\n",
- (int)offset >> 2);
- return 0;
-}
-
-static void imx_timerg_reset(DeviceState *dev)
-{
- IMXTimerGState *s = container_of(dev, IMXTimerGState, busdev.qdev);
-
- /*
- * Soft reset doesn't touch some bits; hard reset clears them
- */
- s->cr &= ~(GPT_CR_EN|GPT_CR_DOZEN|GPT_CR_WAITEN|GPT_CR_DBGEN);
- s->sr = 0;
- s->pr = 0;
- s->ir = 0;
- s->cnt = 0;
- s->ocr1 = TIMER_MAX;
- ptimer_stop(s->timer);
- ptimer_set_limit(s->timer, TIMER_MAX, 1);
- imx_timerg_set_freq(s);
-}
-
-static void imx_timerg_write(void *opaque, hwaddr offset,
- uint64_t value, unsigned size)
-{
- IMXTimerGState *s = (IMXTimerGState *)opaque;
- DPRINTF("g-write(offset=%x, value = 0x%x)\n", (unsigned int)offset >> 2,
- (unsigned int)value);
-
- switch (offset >> 2) {
- case 0: {
- uint32_t oldcr = s->cr;
- /* CR */
- if (value & GPT_CR_SWR) { /* force reset */
- value &= ~GPT_CR_SWR;
- imx_timerg_reset(&s->busdev.qdev);
- imx_timerg_update(s);
- }
-
- s->cr = value & ~0x7c00;
- imx_timerg_set_freq(s);
- if ((oldcr ^ value) & GPT_CR_EN) {
- if (value & GPT_CR_EN) {
- if (value & GPT_CR_ENMOD) {
- ptimer_set_count(s->timer, s->ocr1);
- s->cnt = 0;
- }
- ptimer_run(s->timer,
- (value & GPT_CR_FRR) && (s->ocr1 != TIMER_MAX));
- } else {
- ptimer_stop(s->timer);
- };
- }
- return;
- }
-
- case 1: /* Prescaler */
- s->pr = value & 0xfff;
- imx_timerg_set_freq(s);
- return;
-
- case 2: /* SR */
- /*
- * No point in implementing the status register bits to do with
- * external interrupt sources.
- */
- value &= GPT_SR_OF1 | GPT_SR_ROV;
- s->sr &= ~value;
- imx_timerg_update(s);
- return;
-
- case 3: /* IR -- interrupt register */
- s->ir = value & 0x3f;
- imx_timerg_update(s);
- return;
-
- case 4: /* OCR1 -- output compare register */
- /* In non-freerun mode, reset count when this register is written */
- if (!(s->cr & GPT_CR_FRR)) {
- s->waiting_rov = 0;
- ptimer_set_limit(s->timer, value, 1);
- } else {
- imx_timerg_update_counts(s);
- if (value > s->cnt) {
- s->waiting_rov = 0;
- imx_timerg_reload(s, value);
- } else {
- s->waiting_rov = 1;
- imx_timerg_reload(s, TIMER_MAX - s->cnt);
- }
- }
- s->ocr1 = value;
- return;
-
- default:
- IPRINTF("imx_timerg_write: Bad offset %x\n",
- (int)offset >> 2);
- }
-}
-
-static void imx_timerg_timeout(void *opaque)
-{
- IMXTimerGState *s = (IMXTimerGState *)opaque;
-
- DPRINTF("imx_timerg_timeout, waiting rov=%d\n", s->waiting_rov);
- if (s->cr & GPT_CR_FRR) {
- /*
- * Free running timer from 0 -> TIMERMAX
- * Generates interrupt at TIMER_MAX and at cnt==ocr1
- * If ocr1 == TIMER_MAX, then no need to reload timer.
- */
- if (s->ocr1 == TIMER_MAX) {
- DPRINTF("s->ocr1 == TIMER_MAX, FRR\n");
- s->sr |= GPT_SR_OF1 | GPT_SR_ROV;
- imx_timerg_update(s);
- return;
- }
-
- if (s->waiting_rov) {
- /*
- * We were waiting for cnt==TIMER_MAX
- */
- s->sr |= GPT_SR_ROV;
- s->waiting_rov = 0;
- s->cnt = 0;
- imx_timerg_reload(s, s->ocr1);
- } else {
- /* Must have got a cnt==ocr1 timeout. */
- s->sr |= GPT_SR_OF1;
- s->cnt = s->ocr1;
- s->waiting_rov = 1;
- imx_timerg_reload(s, TIMER_MAX);
- }
- imx_timerg_update(s);
- return;
- }
-
- s->sr |= GPT_SR_OF1;
- imx_timerg_update(s);
-}
-
-static const MemoryRegionOps imx_timerg_ops = {
- .read = imx_timerg_read,
- .write = imx_timerg_write,
- .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-
-static int imx_timerg_init(SysBusDevice *dev)
-{
- IMXTimerGState *s = FROM_SYSBUS(IMXTimerGState, dev);
- QEMUBH *bh;
-
- sysbus_init_irq(dev, &s->irq);
- memory_region_init_io(&s->iomem, &imx_timerg_ops,
- s, "imxg-timer",
- 0x00001000);
- sysbus_init_mmio(dev, &s->iomem);
-
- bh = qemu_bh_new(imx_timerg_timeout, s);
- s->timer = ptimer_init(bh);
-
- /* Hard reset resets extra bits in CR */
- s->cr = 0;
- return 0;
-}
-
-
-
-/*
- * EPIT: Enhanced periodic interrupt timer
- */
-
-#define CR_EN (1 << 0)
-#define CR_ENMOD (1 << 1)
-#define CR_OCIEN (1 << 2)
-#define CR_RLD (1 << 3)
-#define CR_PRESCALE_SHIFT (4)
-#define CR_PRESCALE_MASK (0xfff)
-#define CR_SWR (1 << 16)
-#define CR_IOVW (1 << 17)
-#define CR_DBGEN (1 << 18)
-#define CR_EPIT (1 << 19)
-#define CR_DOZEN (1 << 20)
-#define CR_STOPEN (1 << 21)
-#define CR_CLKSRC_SHIFT (24)
-#define CR_CLKSRC_MASK (0x3 << CR_CLKSRC_SHIFT)
-
-
-/*
- * Exact clock frequencies vary from board to board.
- * These are typical.
- */
-static const IMXClk imx_timerp_clocks[] = {
- 0, /* disabled */
- IPG, /* ipg_clk, ~532MHz */
- IPG, /* ipg_clk_highfreq */
- CLK_32k, /* ipg_clk_32k -- ~32kHz */
-};
-
-typedef struct {
- SysBusDevice busdev;
- ptimer_state *timer;
- MemoryRegion iomem;
- DeviceState *ccm;
-
- uint32_t cr;
- uint32_t lr;
- uint32_t cmp;
-
- uint32_t freq;
- int int_level;
- qemu_irq irq;
-} IMXTimerPState;
-
-/*
- * Update interrupt status
- */
-static void imx_timerp_update(IMXTimerPState *s)
-{
- if (s->int_level && (s->cr & CR_OCIEN)) {
- qemu_irq_raise(s->irq);
- } else {
- qemu_irq_lower(s->irq);
- }
-}
-
-static void imx_timerp_reset(DeviceState *dev)
-{
- IMXTimerPState *s = container_of(dev, IMXTimerPState, busdev.qdev);
-
- s->cr = 0;
- s->lr = TIMER_MAX;
- s->int_level = 0;
- s->cmp = 0;
- ptimer_stop(s->timer);
- ptimer_set_count(s->timer, TIMER_MAX);
-}
-
-static uint64_t imx_timerp_read(void *opaque, hwaddr offset,
- unsigned size)
-{
- IMXTimerPState *s = (IMXTimerPState *)opaque;
-
- DPRINTF("p-read(offset=%x)", offset >> 2);
- switch (offset >> 2) {
- case 0: /* Control Register */
- DPRINTF("cr %x\n", s->cr);
- return s->cr;
-
- case 1: /* Status Register */
- DPRINTF("int_level %x\n", s->int_level);
- return s->int_level;
-
- case 2: /* LR - ticks*/
- DPRINTF("lr %x\n", s->lr);
- return s->lr;
-
- case 3: /* CMP */
- DPRINTF("cmp %x\n", s->cmp);
- return s->cmp;
-
- case 4: /* CNT */
- return ptimer_get_count(s->timer);
- }
- IPRINTF("imx_timerp_read: Bad offset %x\n",
- (int)offset >> 2);
- return 0;
-}
-
-static void set_timerp_freq(IMXTimerPState *s)
-{
- int clksrc;
- unsigned prescaler;
- uint32_t freq;
-
- clksrc = (s->cr & CR_CLKSRC_MASK) >> CR_CLKSRC_SHIFT;
- prescaler = 1 + ((s->cr >> CR_PRESCALE_SHIFT) & CR_PRESCALE_MASK);
- freq = imx_clock_frequency(s->ccm, imx_timerp_clocks[clksrc]) / prescaler;
-
- s->freq = freq;
- DPRINTF("Setting ptimer frequency to %u\n", freq);
-
- if (freq) {
- ptimer_set_freq(s->timer, freq);
- }
-}
-
-static void imx_timerp_write(void *opaque, hwaddr offset,
- uint64_t value, unsigned size)
-{
- IMXTimerPState *s = (IMXTimerPState *)opaque;
- DPRINTF("p-write(offset=%x, value = %x)\n", (unsigned int)offset >> 2,
- (unsigned int)value);
-
- switch (offset >> 2) {
- case 0: /* CR */
- if (value & CR_SWR) {
- imx_timerp_reset(&s->busdev.qdev);
- value &= ~CR_SWR;
- }
- s->cr = value & 0x03ffffff;
- set_timerp_freq(s);
-
- if (s->freq && (s->cr & CR_EN)) {
- if (!(s->cr & CR_ENMOD)) {
- ptimer_set_count(s->timer, s->lr);
- }
- ptimer_run(s->timer, 0);
- } else {
- ptimer_stop(s->timer);
- }
- break;
-
- case 1: /* SR - ACK*/
- s->int_level = 0;
- imx_timerp_update(s);
- break;
-
- case 2: /* LR - set ticks */
- s->lr = value;
- ptimer_set_limit(s->timer, value, !!(s->cr & CR_IOVW));
- break;
-
- case 3: /* CMP */
- s->cmp = value;
- if (value) {
- IPRINTF(
- "Values for EPIT comparison other than zero not supported\n"
- );
- }
- break;
-
- default:
- IPRINTF("imx_timerp_write: Bad offset %x\n",
- (int)offset >> 2);
- }
-}
-
-static void imx_timerp_tick(void *opaque)
-{
- IMXTimerPState *s = (IMXTimerPState *)opaque;
-
- DPRINTF("imxp tick\n");
- if (!(s->cr & CR_RLD)) {
- ptimer_set_count(s->timer, TIMER_MAX);
- }
- s->int_level = 1;
- imx_timerp_update(s);
-}
-
-void imx_timerp_create(const hwaddr addr,
- qemu_irq irq,
- DeviceState *ccm)
-{
- IMXTimerPState *pp;
- DeviceState *dev;
-
- dev = sysbus_create_simple("imx_timerp", addr, irq);
- pp = container_of(dev, IMXTimerPState, busdev.qdev);
- pp->ccm = ccm;
-}
-
-static const MemoryRegionOps imx_timerp_ops = {
- .read = imx_timerp_read,
- .write = imx_timerp_write,
- .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static const VMStateDescription vmstate_imx_timerp = {
- .name = "imx-timerp",
- .version_id = 1,
- .minimum_version_id = 1,
- .minimum_version_id_old = 1,
- .fields = (VMStateField[]) {
- VMSTATE_UINT32(cr, IMXTimerPState),
- VMSTATE_UINT32(lr, IMXTimerPState),
- VMSTATE_UINT32(cmp, IMXTimerPState),
- VMSTATE_UINT32(freq, IMXTimerPState),
- VMSTATE_INT32(int_level, IMXTimerPState),
- VMSTATE_PTIMER(timer, IMXTimerPState),
- VMSTATE_END_OF_LIST()
- }
-};
-
-static int imx_timerp_init(SysBusDevice *dev)
-{
- IMXTimerPState *s = FROM_SYSBUS(IMXTimerPState, dev);
- QEMUBH *bh;
-
- DPRINTF("imx_timerp_init\n");
-
- sysbus_init_irq(dev, &s->irq);
- memory_region_init_io(&s->iomem, &imx_timerp_ops,
- s, "imxp-timer",
- 0x00001000);
- sysbus_init_mmio(dev, &s->iomem);
-
- bh = qemu_bh_new(imx_timerp_tick, s);
- s->timer = ptimer_init(bh);
-
- return 0;
-}
-
-
-void imx_timerg_create(const hwaddr addr,
- qemu_irq irq,
- DeviceState *ccm)
-{
- IMXTimerGState *pp;
- DeviceState *dev;
-
- dev = sysbus_create_simple("imx_timerg", addr, irq);
- pp = container_of(dev, IMXTimerGState, busdev.qdev);
- pp->ccm = ccm;
-}
-
-static void imx_timerg_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
- k->init = imx_timerg_init;
- dc->vmsd = &vmstate_imx_timerg;
- dc->reset = imx_timerg_reset;
- dc->desc = "i.MX general timer";
-}
-
-static void imx_timerp_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
- k->init = imx_timerp_init;
- dc->vmsd = &vmstate_imx_timerp;
- dc->reset = imx_timerp_reset;
- dc->desc = "i.MX periodic timer";
-}
-
-static const TypeInfo imx_timerp_info = {
- .name = "imx_timerp",
- .parent = TYPE_SYS_BUS_DEVICE,
- .instance_size = sizeof(IMXTimerPState),
- .class_init = imx_timerp_class_init,
-};
-
-static const TypeInfo imx_timerg_info = {
- .name = "imx_timerg",
- .parent = TYPE_SYS_BUS_DEVICE,
- .instance_size = sizeof(IMXTimerGState),
- .class_init = imx_timerg_class_init,
-};
-
-static void imx_timer_register_types(void)
-{
- type_register_static(&imx_timerp_info);
- type_register_static(&imx_timerg_info);
-}
-
-type_init(imx_timer_register_types)
common-obj-$(CONFIG_STELLARIS_INPUT) += stellaris_input.o
common-obj-$(CONFIG_TSC2005) += tsc2005.o
common-obj-$(CONFIG_VMMOUSE) += vmmouse.o
+
+obj-$(CONFIG_MILKYMIST) += milkymist-softusb.o
+obj-$(CONFIG_PXA2XX) += pxa2xx_keypad.o
+obj-$(CONFIG_TSC210X) += tsc210x.o
--- /dev/null
+/*
+ * QEMU model of the Milkymist SoftUSB block.
+ *
+ * Copyright (c) 2010 Michael Walle <michael@walle.cc>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * Specification available at:
+ * not available yet
+ */
+
+#include "hw/hw.h"
+#include "hw/sysbus.h"
+#include "trace.h"
+#include "ui/console.h"
+#include "hw/input/hid.h"
+#include "qemu/error-report.h"
+
+enum {
+ R_CTRL = 0,
+ R_MAX
+};
+
+enum {
+ CTRL_RESET = (1<<0),
+};
+
+#define COMLOC_DEBUG_PRODUCE 0x1000
+#define COMLOC_DEBUG_BASE 0x1001
+#define COMLOC_MEVT_PRODUCE 0x1101
+#define COMLOC_MEVT_BASE 0x1102
+#define COMLOC_KEVT_PRODUCE 0x1142
+#define COMLOC_KEVT_BASE 0x1143
+
+struct MilkymistSoftUsbState {
+ SysBusDevice busdev;
+ HIDState hid_kbd;
+ HIDState hid_mouse;
+
+ MemoryRegion regs_region;
+ MemoryRegion pmem;
+ MemoryRegion dmem;
+ qemu_irq irq;
+
+ void *pmem_ptr;
+ void *dmem_ptr;
+
+ /* device properties */
+ uint32_t pmem_size;
+ uint32_t dmem_size;
+
+ /* device registers */
+ uint32_t regs[R_MAX];
+
+ /* mouse state */
+ uint8_t mouse_hid_buffer[4];
+
+ /* keyboard state */
+ uint8_t kbd_hid_buffer[8];
+};
+typedef struct MilkymistSoftUsbState MilkymistSoftUsbState;
+
+static uint64_t softusb_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ MilkymistSoftUsbState *s = opaque;
+ uint32_t r = 0;
+
+ addr >>= 2;
+ switch (addr) {
+ case R_CTRL:
+ r = s->regs[addr];
+ break;
+
+ default:
+ error_report("milkymist_softusb: read access to unknown register 0x"
+ TARGET_FMT_plx, addr << 2);
+ break;
+ }
+
+ trace_milkymist_softusb_memory_read(addr << 2, r);
+
+ return r;
+}
+
+static void
+softusb_write(void *opaque, hwaddr addr, uint64_t value,
+ unsigned size)
+{
+ MilkymistSoftUsbState *s = opaque;
+
+ trace_milkymist_softusb_memory_write(addr, value);
+
+ addr >>= 2;
+ switch (addr) {
+ case R_CTRL:
+ s->regs[addr] = value;
+ break;
+
+ default:
+ error_report("milkymist_softusb: write access to unknown register 0x"
+ TARGET_FMT_plx, addr << 2);
+ break;
+ }
+}
+
+static const MemoryRegionOps softusb_mmio_ops = {
+ .read = softusb_read,
+ .write = softusb_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+};
+
+static inline void softusb_read_dmem(MilkymistSoftUsbState *s,
+ uint32_t offset, uint8_t *buf, uint32_t len)
+{
+ if (offset + len >= s->dmem_size) {
+ error_report("milkymist_softusb: read dmem out of bounds "
+ "at offset 0x%x, len %d", offset, len);
+ memset(buf, 0, len);
+ return;
+ }
+
+ memcpy(buf, s->dmem_ptr + offset, len);
+}
+
+static inline void softusb_write_dmem(MilkymistSoftUsbState *s,
+ uint32_t offset, uint8_t *buf, uint32_t len)
+{
+ if (offset + len >= s->dmem_size) {
+ error_report("milkymist_softusb: write dmem out of bounds "
+ "at offset 0x%x, len %d", offset, len);
+ return;
+ }
+
+ memcpy(s->dmem_ptr + offset, buf, len);
+}
+
+static inline void softusb_read_pmem(MilkymistSoftUsbState *s,
+ uint32_t offset, uint8_t *buf, uint32_t len)
+{
+ if (offset + len >= s->pmem_size) {
+ error_report("milkymist_softusb: read pmem out of bounds "
+ "at offset 0x%x, len %d", offset, len);
+ memset(buf, 0, len);
+ return;
+ }
+
+ memcpy(buf, s->pmem_ptr + offset, len);
+}
+
+static inline void softusb_write_pmem(MilkymistSoftUsbState *s,
+ uint32_t offset, uint8_t *buf, uint32_t len)
+{
+ if (offset + len >= s->pmem_size) {
+ error_report("milkymist_softusb: write pmem out of bounds "
+ "at offset 0x%x, len %d", offset, len);
+ return;
+ }
+
+ memcpy(s->pmem_ptr + offset, buf, len);
+}
+
+static void softusb_mouse_changed(MilkymistSoftUsbState *s)
+{
+ uint8_t m;
+
+ softusb_read_dmem(s, COMLOC_MEVT_PRODUCE, &m, 1);
+ trace_milkymist_softusb_mevt(m);
+ softusb_write_dmem(s, COMLOC_MEVT_BASE + 4 * m, s->mouse_hid_buffer, 4);
+ m = (m + 1) & 0xf;
+ softusb_write_dmem(s, COMLOC_MEVT_PRODUCE, &m, 1);
+
+ trace_milkymist_softusb_pulse_irq();
+ qemu_irq_pulse(s->irq);
+}
+
+static void softusb_kbd_changed(MilkymistSoftUsbState *s)
+{
+ uint8_t m;
+
+ softusb_read_dmem(s, COMLOC_KEVT_PRODUCE, &m, 1);
+ trace_milkymist_softusb_kevt(m);
+ softusb_write_dmem(s, COMLOC_KEVT_BASE + 8 * m, s->kbd_hid_buffer, 8);
+ m = (m + 1) & 0x7;
+ softusb_write_dmem(s, COMLOC_KEVT_PRODUCE, &m, 1);
+
+ trace_milkymist_softusb_pulse_irq();
+ qemu_irq_pulse(s->irq);
+}
+
+static void softusb_kbd_hid_datain(HIDState *hs)
+{
+ MilkymistSoftUsbState *s = container_of(hs, MilkymistSoftUsbState, hid_kbd);
+ int len;
+
+ /* if device is in reset, do nothing */
+ if (s->regs[R_CTRL] & CTRL_RESET) {
+ return;
+ }
+
+ len = hid_keyboard_poll(hs, s->kbd_hid_buffer, sizeof(s->kbd_hid_buffer));
+
+ if (len == 8) {
+ softusb_kbd_changed(s);
+ }
+}
+
+static void softusb_mouse_hid_datain(HIDState *hs)
+{
+ MilkymistSoftUsbState *s =
+ container_of(hs, MilkymistSoftUsbState, hid_mouse);
+ int len;
+
+ /* if device is in reset, do nothing */
+ if (s->regs[R_CTRL] & CTRL_RESET) {
+ return;
+ }
+
+ len = hid_pointer_poll(hs, s->mouse_hid_buffer,
+ sizeof(s->mouse_hid_buffer));
+
+ if (len == 4) {
+ softusb_mouse_changed(s);
+ }
+}
+
+static void milkymist_softusb_reset(DeviceState *d)
+{
+ MilkymistSoftUsbState *s =
+ container_of(d, MilkymistSoftUsbState, busdev.qdev);
+ int i;
+
+ for (i = 0; i < R_MAX; i++) {
+ s->regs[i] = 0;
+ }
+ memset(s->kbd_hid_buffer, 0, sizeof(s->kbd_hid_buffer));
+ memset(s->mouse_hid_buffer, 0, sizeof(s->mouse_hid_buffer));
+
+ hid_reset(&s->hid_kbd);
+ hid_reset(&s->hid_mouse);
+
+ /* defaults */
+ s->regs[R_CTRL] = CTRL_RESET;
+}
+
+static int milkymist_softusb_init(SysBusDevice *dev)
+{
+ MilkymistSoftUsbState *s = FROM_SYSBUS(typeof(*s), dev);
+
+ sysbus_init_irq(dev, &s->irq);
+
+ memory_region_init_io(&s->regs_region, &softusb_mmio_ops, s,
+ "milkymist-softusb", R_MAX * 4);
+ sysbus_init_mmio(dev, &s->regs_region);
+
+ /* register pmem and dmem */
+ memory_region_init_ram(&s->pmem, "milkymist-softusb.pmem",
+ s->pmem_size);
+ vmstate_register_ram_global(&s->pmem);
+ s->pmem_ptr = memory_region_get_ram_ptr(&s->pmem);
+ sysbus_init_mmio(dev, &s->pmem);
+ memory_region_init_ram(&s->dmem, "milkymist-softusb.dmem",
+ s->dmem_size);
+ vmstate_register_ram_global(&s->dmem);
+ s->dmem_ptr = memory_region_get_ram_ptr(&s->dmem);
+ sysbus_init_mmio(dev, &s->dmem);
+
+ hid_init(&s->hid_kbd, HID_KEYBOARD, softusb_kbd_hid_datain);
+ hid_init(&s->hid_mouse, HID_MOUSE, softusb_mouse_hid_datain);
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_milkymist_softusb = {
+ .name = "milkymist-softusb",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(regs, MilkymistSoftUsbState, R_MAX),
+ VMSTATE_HID_KEYBOARD_DEVICE(hid_kbd, MilkymistSoftUsbState),
+ VMSTATE_HID_POINTER_DEVICE(hid_mouse, MilkymistSoftUsbState),
+ VMSTATE_BUFFER(kbd_hid_buffer, MilkymistSoftUsbState),
+ VMSTATE_BUFFER(mouse_hid_buffer, MilkymistSoftUsbState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static Property milkymist_softusb_properties[] = {
+ DEFINE_PROP_UINT32("pmem_size", MilkymistSoftUsbState, pmem_size, 0x00001000),
+ DEFINE_PROP_UINT32("dmem_size", MilkymistSoftUsbState, dmem_size, 0x00002000),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void milkymist_softusb_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = milkymist_softusb_init;
+ dc->reset = milkymist_softusb_reset;
+ dc->vmsd = &vmstate_milkymist_softusb;
+ dc->props = milkymist_softusb_properties;
+}
+
+static const TypeInfo milkymist_softusb_info = {
+ .name = "milkymist-softusb",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(MilkymistSoftUsbState),
+ .class_init = milkymist_softusb_class_init,
+};
+
+static void milkymist_softusb_register_types(void)
+{
+ type_register_static(&milkymist_softusb_info);
+}
+
+type_init(milkymist_softusb_register_types)
--- /dev/null
+/*
+ * Intel PXA27X Keypad Controller emulation.
+ *
+ * Copyright (c) 2007 MontaVista Software, Inc
+ * Written by Armin Kuster <akuster@kama-aina.net>
+ * or <Akuster@mvista.com>
+ *
+ * This code is licensed under the GPLv2.
+ *
+ * Contributions after 2012-01-13 are licensed under the terms of the
+ * GNU GPL, version 2 or (at your option) any later version.
+ */
+
+#include "hw/hw.h"
+#include "hw/arm/pxa.h"
+#include "ui/console.h"
+
+/*
+ * Keypad
+ */
+#define KPC 0x00 /* Keypad Interface Control register */
+#define KPDK 0x08 /* Keypad Interface Direct Key register */
+#define KPREC 0x10 /* Keypad Interface Rotary Encoder register */
+#define KPMK 0x18 /* Keypad Interface Matrix Key register */
+#define KPAS 0x20 /* Keypad Interface Automatic Scan register */
+#define KPASMKP0 0x28 /* Keypad Interface Automatic Scan Multiple
+ Key Presser register 0 */
+#define KPASMKP1 0x30 /* Keypad Interface Automatic Scan Multiple
+ Key Presser register 1 */
+#define KPASMKP2 0x38 /* Keypad Interface Automatic Scan Multiple
+ Key Presser register 2 */
+#define KPASMKP3 0x40 /* Keypad Interface Automatic Scan Multiple
+ Key Presser register 3 */
+#define KPKDI 0x48 /* Keypad Interface Key Debounce Interval
+ register */
+
+/* Keypad defines */
+#define KPC_AS (0x1 << 30) /* Automatic Scan bit */
+#define KPC_ASACT (0x1 << 29) /* Automatic Scan on Activity */
+#define KPC_MI (0x1 << 22) /* Matrix interrupt bit */
+#define KPC_IMKP (0x1 << 21) /* Ignore Multiple Key Press */
+#define KPC_MS7 (0x1 << 20) /* Matrix scan line 7 */
+#define KPC_MS6 (0x1 << 19) /* Matrix scan line 6 */
+#define KPC_MS5 (0x1 << 18) /* Matrix scan line 5 */
+#define KPC_MS4 (0x1 << 17) /* Matrix scan line 4 */
+#define KPC_MS3 (0x1 << 16) /* Matrix scan line 3 */
+#define KPC_MS2 (0x1 << 15) /* Matrix scan line 2 */
+#define KPC_MS1 (0x1 << 14) /* Matrix scan line 1 */
+#define KPC_MS0 (0x1 << 13) /* Matrix scan line 0 */
+#define KPC_ME (0x1 << 12) /* Matrix Keypad Enable */
+#define KPC_MIE (0x1 << 11) /* Matrix Interrupt Enable */
+#define KPC_DK_DEB_SEL (0x1 << 9) /* Direct Keypad Debounce Select */
+#define KPC_DI (0x1 << 5) /* Direct key interrupt bit */
+#define KPC_RE_ZERO_DEB (0x1 << 4) /* Rotary Encoder Zero Debounce */
+#define KPC_REE1 (0x1 << 3) /* Rotary Encoder1 Enable */
+#define KPC_REE0 (0x1 << 2) /* Rotary Encoder0 Enable */
+#define KPC_DE (0x1 << 1) /* Direct Keypad Enable */
+#define KPC_DIE (0x1 << 0) /* Direct Keypad interrupt Enable */
+
+#define KPDK_DKP (0x1 << 31)
+#define KPDK_DK7 (0x1 << 7)
+#define KPDK_DK6 (0x1 << 6)
+#define KPDK_DK5 (0x1 << 5)
+#define KPDK_DK4 (0x1 << 4)
+#define KPDK_DK3 (0x1 << 3)
+#define KPDK_DK2 (0x1 << 2)
+#define KPDK_DK1 (0x1 << 1)
+#define KPDK_DK0 (0x1 << 0)
+
+#define KPREC_OF1 (0x1 << 31)
+#define KPREC_UF1 (0x1 << 30)
+#define KPREC_OF0 (0x1 << 15)
+#define KPREC_UF0 (0x1 << 14)
+
+#define KPMK_MKP (0x1 << 31)
+#define KPAS_SO (0x1 << 31)
+#define KPASMKPx_SO (0x1 << 31)
+
+
+#define KPASMKPx_MKC(row, col) (1 << (row + 16 * (col % 2)))
+
+#define PXAKBD_MAXROW 8
+#define PXAKBD_MAXCOL 8
+
+struct PXA2xxKeyPadState {
+ MemoryRegion iomem;
+ qemu_irq irq;
+ struct keymap *map;
+ int pressed_cnt;
+ int alt_code;
+
+ uint32_t kpc;
+ uint32_t kpdk;
+ uint32_t kprec;
+ uint32_t kpmk;
+ uint32_t kpas;
+ uint32_t kpasmkp[4];
+ uint32_t kpkdi;
+};
+
+static void pxa27x_keypad_find_pressed_key(PXA2xxKeyPadState *kp, int *row, int *col)
+{
+ int i;
+ for (i = 0; i < 4; i++)
+ {
+ *col = i * 2;
+ for (*row = 0; *row < 8; (*row)++) {
+ if (kp->kpasmkp[i] & (1 << *row))
+ return;
+ }
+ *col = i * 2 + 1;
+ for (*row = 0; *row < 8; (*row)++) {
+ if (kp->kpasmkp[i] & (1 << (*row + 16)))
+ return;
+ }
+ }
+}
+
+static void pxa27x_keyboard_event (PXA2xxKeyPadState *kp, int keycode)
+{
+ int row, col, rel, assert_irq = 0;
+ uint32_t val;
+
+ if (keycode == 0xe0) {
+ kp->alt_code = 1;
+ return;
+ }
+
+ if(!(kp->kpc & KPC_ME)) /* skip if not enabled */
+ return;
+
+ rel = (keycode & 0x80) ? 1 : 0; /* key release from qemu */
+ keycode &= ~0x80; /* strip qemu key release bit */
+ if (kp->alt_code) {
+ keycode |= 0x80;
+ kp->alt_code = 0;
+ }
+
+ row = kp->map[keycode].row;
+ col = kp->map[keycode].column;
+ if (row == -1 || col == -1) {
+ return;
+ }
+
+ val = KPASMKPx_MKC(row, col);
+ if (rel) {
+ if (kp->kpasmkp[col / 2] & val) {
+ kp->kpasmkp[col / 2] &= ~val;
+ kp->pressed_cnt--;
+ assert_irq = 1;
+ }
+ } else {
+ if (!(kp->kpasmkp[col / 2] & val)) {
+ kp->kpasmkp[col / 2] |= val;
+ kp->pressed_cnt++;
+ assert_irq = 1;
+ }
+ }
+ kp->kpas = ((kp->pressed_cnt & 0x1f) << 26) | (0xf << 4) | 0xf;
+ if (kp->pressed_cnt == 1) {
+ kp->kpas &= ~((0xf << 4) | 0xf);
+ if (rel) {
+ pxa27x_keypad_find_pressed_key(kp, &row, &col);
+ }
+ kp->kpas |= ((row & 0xf) << 4) | (col & 0xf);
+ }
+
+ if (!(kp->kpc & (KPC_AS | KPC_ASACT)))
+ assert_irq = 0;
+
+ if (assert_irq && (kp->kpc & KPC_MIE)) {
+ kp->kpc |= KPC_MI;
+ qemu_irq_raise(kp->irq);
+ }
+}
+
+static uint64_t pxa2xx_keypad_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ PXA2xxKeyPadState *s = (PXA2xxKeyPadState *) opaque;
+ uint32_t tmp;
+
+ switch (offset) {
+ case KPC:
+ tmp = s->kpc;
+ if(tmp & KPC_MI)
+ s->kpc &= ~(KPC_MI);
+ if(tmp & KPC_DI)
+ s->kpc &= ~(KPC_DI);
+ qemu_irq_lower(s->irq);
+ return tmp;
+ break;
+ case KPDK:
+ return s->kpdk;
+ break;
+ case KPREC:
+ tmp = s->kprec;
+ if(tmp & KPREC_OF1)
+ s->kprec &= ~(KPREC_OF1);
+ if(tmp & KPREC_UF1)
+ s->kprec &= ~(KPREC_UF1);
+ if(tmp & KPREC_OF0)
+ s->kprec &= ~(KPREC_OF0);
+ if(tmp & KPREC_UF0)
+ s->kprec &= ~(KPREC_UF0);
+ return tmp;
+ break;
+ case KPMK:
+ tmp = s->kpmk;
+ if(tmp & KPMK_MKP)
+ s->kpmk &= ~(KPMK_MKP);
+ return tmp;
+ break;
+ case KPAS:
+ return s->kpas;
+ break;
+ case KPASMKP0:
+ return s->kpasmkp[0];
+ break;
+ case KPASMKP1:
+ return s->kpasmkp[1];
+ break;
+ case KPASMKP2:
+ return s->kpasmkp[2];
+ break;
+ case KPASMKP3:
+ return s->kpasmkp[3];
+ break;
+ case KPKDI:
+ return s->kpkdi;
+ break;
+ default:
+ hw_error("%s: Bad offset " REG_FMT "\n", __FUNCTION__, offset);
+ }
+
+ return 0;
+}
+
+static void pxa2xx_keypad_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ PXA2xxKeyPadState *s = (PXA2xxKeyPadState *) opaque;
+
+ switch (offset) {
+ case KPC:
+ s->kpc = value;
+ if (s->kpc & KPC_AS) {
+ s->kpc &= ~(KPC_AS);
+ }
+ break;
+ case KPDK:
+ s->kpdk = value;
+ break;
+ case KPREC:
+ s->kprec = value;
+ break;
+ case KPMK:
+ s->kpmk = value;
+ break;
+ case KPAS:
+ s->kpas = value;
+ break;
+ case KPASMKP0:
+ s->kpasmkp[0] = value;
+ break;
+ case KPASMKP1:
+ s->kpasmkp[1] = value;
+ break;
+ case KPASMKP2:
+ s->kpasmkp[2] = value;
+ break;
+ case KPASMKP3:
+ s->kpasmkp[3] = value;
+ break;
+ case KPKDI:
+ s->kpkdi = value;
+ break;
+
+ default:
+ hw_error("%s: Bad offset " REG_FMT "\n", __FUNCTION__, offset);
+ }
+}
+
+static const MemoryRegionOps pxa2xx_keypad_ops = {
+ .read = pxa2xx_keypad_read,
+ .write = pxa2xx_keypad_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static const VMStateDescription vmstate_pxa2xx_keypad = {
+ .name = "pxa2xx_keypad",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(kpc, PXA2xxKeyPadState),
+ VMSTATE_UINT32(kpdk, PXA2xxKeyPadState),
+ VMSTATE_UINT32(kprec, PXA2xxKeyPadState),
+ VMSTATE_UINT32(kpmk, PXA2xxKeyPadState),
+ VMSTATE_UINT32(kpas, PXA2xxKeyPadState),
+ VMSTATE_UINT32_ARRAY(kpasmkp, PXA2xxKeyPadState, 4),
+ VMSTATE_UINT32(kpkdi, PXA2xxKeyPadState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+PXA2xxKeyPadState *pxa27x_keypad_init(MemoryRegion *sysmem,
+ hwaddr base,
+ qemu_irq irq)
+{
+ PXA2xxKeyPadState *s;
+
+ s = (PXA2xxKeyPadState *) g_malloc0(sizeof(PXA2xxKeyPadState));
+ s->irq = irq;
+
+ memory_region_init_io(&s->iomem, &pxa2xx_keypad_ops, s,
+ "pxa2xx-keypad", 0x00100000);
+ memory_region_add_subregion(sysmem, base, &s->iomem);
+
+ vmstate_register(NULL, 0, &vmstate_pxa2xx_keypad, s);
+
+ return s;
+}
+
+void pxa27x_register_keypad(PXA2xxKeyPadState *kp, struct keymap *map,
+ int size)
+{
+ if(!map || size < 0x80) {
+ fprintf(stderr, "%s - No PXA keypad map defined\n", __FUNCTION__);
+ exit(-1);
+ }
+
+ kp->map = map;
+ qemu_add_kbd_event_handler((QEMUPutKBDEvent *) pxa27x_keyboard_event, kp);
+}
--- /dev/null
+/*
+ * TI TSC2102 (touchscreen/sensors/audio controller) emulator.
+ * TI TSC2301 (touchscreen/sensors/keypad).
+ *
+ * Copyright (c) 2006 Andrzej Zaborowski <balrog@zabor.org>
+ * Copyright (C) 2008 Nokia Corporation
+ *
+ * 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) version 3 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/hw.h"
+#include "audio/audio.h"
+#include "qemu/timer.h"
+#include "ui/console.h"
+#include "hw/arm/omap.h" /* For I2SCodec and uWireSlave */
+#include "hw/arm/devices.h"
+
+#define TSC_DATA_REGISTERS_PAGE 0x0
+#define TSC_CONTROL_REGISTERS_PAGE 0x1
+#define TSC_AUDIO_REGISTERS_PAGE 0x2
+
+#define TSC_VERBOSE
+
+#define TSC_CUT_RESOLUTION(value, p) ((value) >> (16 - resolution[p]))
+
+typedef struct {
+ qemu_irq pint;
+ qemu_irq kbint;
+ qemu_irq davint;
+ QEMUTimer *timer;
+ QEMUSoundCard card;
+ uWireSlave chip;
+ I2SCodec codec;
+ uint8_t in_fifo[16384];
+ uint8_t out_fifo[16384];
+ uint16_t model;
+
+ int x, y;
+ int pressure;
+
+ int state, page, offset, irq;
+ uint16_t command, dav;
+
+ int busy;
+ int enabled;
+ int host_mode;
+ int function;
+ int nextfunction;
+ int precision;
+ int nextprecision;
+ int filter;
+ int pin_func;
+ int ref;
+ int timing;
+ int noise;
+
+ uint16_t audio_ctrl1;
+ uint16_t audio_ctrl2;
+ uint16_t audio_ctrl3;
+ uint16_t pll[3];
+ uint16_t volume;
+ int64_t volume_change;
+ int softstep;
+ uint16_t dac_power;
+ int64_t powerdown;
+ uint16_t filter_data[0x14];
+
+ const char *name;
+ SWVoiceIn *adc_voice[1];
+ SWVoiceOut *dac_voice[1];
+ int i2s_rx_rate;
+ int i2s_tx_rate;
+
+ int tr[8];
+
+ struct {
+ uint16_t down;
+ uint16_t mask;
+ int scan;
+ int debounce;
+ int mode;
+ int intr;
+ } kb;
+} TSC210xState;
+
+static const int resolution[4] = { 12, 8, 10, 12 };
+
+#define TSC_MODE_NO_SCAN 0x0
+#define TSC_MODE_XY_SCAN 0x1
+#define TSC_MODE_XYZ_SCAN 0x2
+#define TSC_MODE_X 0x3
+#define TSC_MODE_Y 0x4
+#define TSC_MODE_Z 0x5
+#define TSC_MODE_BAT1 0x6
+#define TSC_MODE_BAT2 0x7
+#define TSC_MODE_AUX 0x8
+#define TSC_MODE_AUX_SCAN 0x9
+#define TSC_MODE_TEMP1 0xa
+#define TSC_MODE_PORT_SCAN 0xb
+#define TSC_MODE_TEMP2 0xc
+#define TSC_MODE_XX_DRV 0xd
+#define TSC_MODE_YY_DRV 0xe
+#define TSC_MODE_YX_DRV 0xf
+
+static const uint16_t mode_regs[16] = {
+ 0x0000, /* No scan */
+ 0x0600, /* X, Y scan */
+ 0x0780, /* X, Y, Z scan */
+ 0x0400, /* X */
+ 0x0200, /* Y */
+ 0x0180, /* Z */
+ 0x0040, /* BAT1 */
+ 0x0030, /* BAT2 */
+ 0x0010, /* AUX */
+ 0x0010, /* AUX scan */
+ 0x0004, /* TEMP1 */
+ 0x0070, /* Port scan */
+ 0x0002, /* TEMP2 */
+ 0x0000, /* X+, X- drivers */
+ 0x0000, /* Y+, Y- drivers */
+ 0x0000, /* Y+, X- drivers */
+};
+
+#define X_TRANSFORM(s) \
+ ((s->y * s->tr[0] - s->x * s->tr[1]) / s->tr[2] + s->tr[3])
+#define Y_TRANSFORM(s) \
+ ((s->y * s->tr[4] - s->x * s->tr[5]) / s->tr[6] + s->tr[7])
+#define Z1_TRANSFORM(s) \
+ ((400 - ((s)->x >> 7) + ((s)->pressure << 10)) << 4)
+#define Z2_TRANSFORM(s) \
+ ((4000 + ((s)->y >> 7) - ((s)->pressure << 10)) << 4)
+
+#define BAT1_VAL 0x8660
+#define BAT2_VAL 0x0000
+#define AUX1_VAL 0x35c0
+#define AUX2_VAL 0xffff
+#define TEMP1_VAL 0x8c70
+#define TEMP2_VAL 0xa5b0
+
+#define TSC_POWEROFF_DELAY 50
+#define TSC_SOFTSTEP_DELAY 50
+
+static void tsc210x_reset(TSC210xState *s)
+{
+ s->state = 0;
+ s->pin_func = 2;
+ s->enabled = 0;
+ s->busy = 0;
+ s->nextfunction = 0;
+ s->ref = 0;
+ s->timing = 0;
+ s->irq = 0;
+ s->dav = 0;
+
+ s->audio_ctrl1 = 0x0000;
+ s->audio_ctrl2 = 0x4410;
+ s->audio_ctrl3 = 0x0000;
+ s->pll[0] = 0x1004;
+ s->pll[1] = 0x0000;
+ s->pll[2] = 0x1fff;
+ s->volume = 0xffff;
+ s->dac_power = 0x8540;
+ s->softstep = 1;
+ s->volume_change = 0;
+ s->powerdown = 0;
+ s->filter_data[0x00] = 0x6be3;
+ s->filter_data[0x01] = 0x9666;
+ s->filter_data[0x02] = 0x675d;
+ s->filter_data[0x03] = 0x6be3;
+ s->filter_data[0x04] = 0x9666;
+ s->filter_data[0x05] = 0x675d;
+ s->filter_data[0x06] = 0x7d83;
+ s->filter_data[0x07] = 0x84ee;
+ s->filter_data[0x08] = 0x7d83;
+ s->filter_data[0x09] = 0x84ee;
+ s->filter_data[0x0a] = 0x6be3;
+ s->filter_data[0x0b] = 0x9666;
+ s->filter_data[0x0c] = 0x675d;
+ s->filter_data[0x0d] = 0x6be3;
+ s->filter_data[0x0e] = 0x9666;
+ s->filter_data[0x0f] = 0x675d;
+ s->filter_data[0x10] = 0x7d83;
+ s->filter_data[0x11] = 0x84ee;
+ s->filter_data[0x12] = 0x7d83;
+ s->filter_data[0x13] = 0x84ee;
+
+ s->i2s_tx_rate = 0;
+ s->i2s_rx_rate = 0;
+
+ s->kb.scan = 1;
+ s->kb.debounce = 0;
+ s->kb.mask = 0x0000;
+ s->kb.mode = 3;
+ s->kb.intr = 0;
+
+ qemu_set_irq(s->pint, !s->irq);
+ qemu_set_irq(s->davint, !s->dav);
+ qemu_irq_raise(s->kbint);
+}
+
+typedef struct {
+ int rate;
+ int dsor;
+ int fsref;
+} TSC210xRateInfo;
+
+/* { rate, dsor, fsref } */
+static const TSC210xRateInfo tsc2101_rates[] = {
+ /* Fsref / 6.0 */
+ { 7350, 7, 1 },
+ { 8000, 7, 0 },
+ /* Fsref / 5.5 */
+ { 8018, 6, 1 },
+ { 8727, 6, 0 },
+ /* Fsref / 5.0 */
+ { 8820, 5, 1 },
+ { 9600, 5, 0 },
+ /* Fsref / 4.0 */
+ { 11025, 4, 1 },
+ { 12000, 4, 0 },
+ /* Fsref / 3.0 */
+ { 14700, 3, 1 },
+ { 16000, 3, 0 },
+ /* Fsref / 2.0 */
+ { 22050, 2, 1 },
+ { 24000, 2, 0 },
+ /* Fsref / 1.5 */
+ { 29400, 1, 1 },
+ { 32000, 1, 0 },
+ /* Fsref */
+ { 44100, 0, 1 },
+ { 48000, 0, 0 },
+
+ { 0, 0, 0 },
+};
+
+/* { rate, dsor, fsref } */
+static const TSC210xRateInfo tsc2102_rates[] = {
+ /* Fsref / 6.0 */
+ { 7350, 63, 1 },
+ { 8000, 63, 0 },
+ /* Fsref / 6.0 */
+ { 7350, 54, 1 },
+ { 8000, 54, 0 },
+ /* Fsref / 5.0 */
+ { 8820, 45, 1 },
+ { 9600, 45, 0 },
+ /* Fsref / 4.0 */
+ { 11025, 36, 1 },
+ { 12000, 36, 0 },
+ /* Fsref / 3.0 */
+ { 14700, 27, 1 },
+ { 16000, 27, 0 },
+ /* Fsref / 2.0 */
+ { 22050, 18, 1 },
+ { 24000, 18, 0 },
+ /* Fsref / 1.5 */
+ { 29400, 9, 1 },
+ { 32000, 9, 0 },
+ /* Fsref */
+ { 44100, 0, 1 },
+ { 48000, 0, 0 },
+
+ { 0, 0, 0 },
+};
+
+static inline void tsc210x_out_flush(TSC210xState *s, int len)
+{
+ uint8_t *data = s->codec.out.fifo + s->codec.out.start;
+ uint8_t *end = data + len;
+
+ while (data < end)
+ data += AUD_write(s->dac_voice[0], data, end - data) ?: (end - data);
+
+ s->codec.out.len -= len;
+ if (s->codec.out.len)
+ memmove(s->codec.out.fifo, end, s->codec.out.len);
+ s->codec.out.start = 0;
+}
+
+static void tsc210x_audio_out_cb(TSC210xState *s, int free_b)
+{
+ if (s->codec.out.len >= free_b) {
+ tsc210x_out_flush(s, free_b);
+ return;
+ }
+
+ s->codec.out.size = MIN(free_b, 16384);
+ qemu_irq_raise(s->codec.tx_start);
+}
+
+static void tsc2102_audio_rate_update(TSC210xState *s)
+{
+ const TSC210xRateInfo *rate;
+
+ s->codec.tx_rate = 0;
+ s->codec.rx_rate = 0;
+ if (s->dac_power & (1 << 15)) /* PWDNC */
+ return;
+
+ for (rate = tsc2102_rates; rate->rate; rate ++)
+ if (rate->dsor == (s->audio_ctrl1 & 0x3f) && /* DACFS */
+ rate->fsref == ((s->audio_ctrl3 >> 13) & 1))/* REFFS */
+ break;
+ if (!rate->rate) {
+ printf("%s: unknown sampling rate configured\n", __FUNCTION__);
+ return;
+ }
+
+ s->codec.tx_rate = rate->rate;
+}
+
+static void tsc2102_audio_output_update(TSC210xState *s)
+{
+ int enable;
+ struct audsettings fmt;
+
+ if (s->dac_voice[0]) {
+ tsc210x_out_flush(s, s->codec.out.len);
+ s->codec.out.size = 0;
+ AUD_set_active_out(s->dac_voice[0], 0);
+ AUD_close_out(&s->card, s->dac_voice[0]);
+ s->dac_voice[0] = NULL;
+ }
+ s->codec.cts = 0;
+
+ enable =
+ (~s->dac_power & (1 << 15)) && /* PWDNC */
+ (~s->dac_power & (1 << 10)); /* DAPWDN */
+ if (!enable || !s->codec.tx_rate)
+ return;
+
+ /* Force our own sampling rate even in slave DAC mode */
+ fmt.endianness = 0;
+ fmt.nchannels = 2;
+ fmt.freq = s->codec.tx_rate;
+ fmt.fmt = AUD_FMT_S16;
+
+ s->dac_voice[0] = AUD_open_out(&s->card, s->dac_voice[0],
+ "tsc2102.sink", s, (void *) tsc210x_audio_out_cb, &fmt);
+ if (s->dac_voice[0]) {
+ s->codec.cts = 1;
+ AUD_set_active_out(s->dac_voice[0], 1);
+ }
+}
+
+static uint16_t tsc2102_data_register_read(TSC210xState *s, int reg)
+{
+ switch (reg) {
+ case 0x00: /* X */
+ s->dav &= 0xfbff;
+ return TSC_CUT_RESOLUTION(X_TRANSFORM(s), s->precision) +
+ (s->noise & 3);
+
+ case 0x01: /* Y */
+ s->noise ++;
+ s->dav &= 0xfdff;
+ return TSC_CUT_RESOLUTION(Y_TRANSFORM(s), s->precision) ^
+ (s->noise & 3);
+
+ case 0x02: /* Z1 */
+ s->dav &= 0xfeff;
+ return TSC_CUT_RESOLUTION(Z1_TRANSFORM(s), s->precision) -
+ (s->noise & 3);
+
+ case 0x03: /* Z2 */
+ s->dav &= 0xff7f;
+ return TSC_CUT_RESOLUTION(Z2_TRANSFORM(s), s->precision) |
+ (s->noise & 3);
+
+ case 0x04: /* KPData */
+ if ((s->model & 0xff00) == 0x2300) {
+ if (s->kb.intr && (s->kb.mode & 2)) {
+ s->kb.intr = 0;
+ qemu_irq_raise(s->kbint);
+ }
+ return s->kb.down;
+ }
+
+ return 0xffff;
+
+ case 0x05: /* BAT1 */
+ s->dav &= 0xffbf;
+ return TSC_CUT_RESOLUTION(BAT1_VAL, s->precision) +
+ (s->noise & 6);
+
+ case 0x06: /* BAT2 */
+ s->dav &= 0xffdf;
+ return TSC_CUT_RESOLUTION(BAT2_VAL, s->precision);
+
+ case 0x07: /* AUX1 */
+ s->dav &= 0xffef;
+ return TSC_CUT_RESOLUTION(AUX1_VAL, s->precision);
+
+ case 0x08: /* AUX2 */
+ s->dav &= 0xfff7;
+ return 0xffff;
+
+ case 0x09: /* TEMP1 */
+ s->dav &= 0xfffb;
+ return TSC_CUT_RESOLUTION(TEMP1_VAL, s->precision) -
+ (s->noise & 5);
+
+ case 0x0a: /* TEMP2 */
+ s->dav &= 0xfffd;
+ return TSC_CUT_RESOLUTION(TEMP2_VAL, s->precision) ^
+ (s->noise & 3);
+
+ case 0x0b: /* DAC */
+ s->dav &= 0xfffe;
+ return 0xffff;
+
+ default:
+#ifdef TSC_VERBOSE
+ fprintf(stderr, "tsc2102_data_register_read: "
+ "no such register: 0x%02x\n", reg);
+#endif
+ return 0xffff;
+ }
+}
+
+static uint16_t tsc2102_control_register_read(
+ TSC210xState *s, int reg)
+{
+ switch (reg) {
+ case 0x00: /* TSC ADC */
+ return (s->pressure << 15) | ((!s->busy) << 14) |
+ (s->nextfunction << 10) | (s->nextprecision << 8) | s->filter;
+
+ case 0x01: /* Status / Keypad Control */
+ if ((s->model & 0xff00) == 0x2100)
+ return (s->pin_func << 14) | ((!s->enabled) << 13) |
+ (s->host_mode << 12) | ((!!s->dav) << 11) | s->dav;
+ else
+ return (s->kb.intr << 15) | ((s->kb.scan || !s->kb.down) << 14) |
+ (s->kb.debounce << 11);
+
+ case 0x02: /* DAC Control */
+ if ((s->model & 0xff00) == 0x2300)
+ return s->dac_power & 0x8000;
+ else
+ goto bad_reg;
+
+ case 0x03: /* Reference */
+ return s->ref;
+
+ case 0x04: /* Reset */
+ return 0xffff;
+
+ case 0x05: /* Configuration */
+ return s->timing;
+
+ case 0x06: /* Secondary configuration */
+ if ((s->model & 0xff00) == 0x2100)
+ goto bad_reg;
+ return ((!s->dav) << 15) | ((s->kb.mode & 1) << 14) | s->pll[2];
+
+ case 0x10: /* Keypad Mask */
+ if ((s->model & 0xff00) == 0x2100)
+ goto bad_reg;
+ return s->kb.mask;
+
+ default:
+ bad_reg:
+#ifdef TSC_VERBOSE
+ fprintf(stderr, "tsc2102_control_register_read: "
+ "no such register: 0x%02x\n", reg);
+#endif
+ return 0xffff;
+ }
+}
+
+static uint16_t tsc2102_audio_register_read(TSC210xState *s, int reg)
+{
+ int l_ch, r_ch;
+ uint16_t val;
+
+ switch (reg) {
+ case 0x00: /* Audio Control 1 */
+ return s->audio_ctrl1;
+
+ case 0x01:
+ return 0xff00;
+
+ case 0x02: /* DAC Volume Control */
+ return s->volume;
+
+ case 0x03:
+ return 0x8b00;
+
+ case 0x04: /* Audio Control 2 */
+ l_ch = 1;
+ r_ch = 1;
+ if (s->softstep && !(s->dac_power & (1 << 10))) {
+ l_ch = (qemu_get_clock_ns(vm_clock) >
+ s->volume_change + TSC_SOFTSTEP_DELAY);
+ r_ch = (qemu_get_clock_ns(vm_clock) >
+ s->volume_change + TSC_SOFTSTEP_DELAY);
+ }
+
+ return s->audio_ctrl2 | (l_ch << 3) | (r_ch << 2);
+
+ case 0x05: /* Stereo DAC Power Control */
+ return 0x2aa0 | s->dac_power |
+ (((s->dac_power & (1 << 10)) &&
+ (qemu_get_clock_ns(vm_clock) >
+ s->powerdown + TSC_POWEROFF_DELAY)) << 6);
+
+ case 0x06: /* Audio Control 3 */
+ val = s->audio_ctrl3 | 0x0001;
+ s->audio_ctrl3 &= 0xff3f;
+ return val;
+
+ case 0x07: /* LCH_BASS_BOOST_N0 */
+ case 0x08: /* LCH_BASS_BOOST_N1 */
+ case 0x09: /* LCH_BASS_BOOST_N2 */
+ case 0x0a: /* LCH_BASS_BOOST_N3 */
+ case 0x0b: /* LCH_BASS_BOOST_N4 */
+ case 0x0c: /* LCH_BASS_BOOST_N5 */
+ case 0x0d: /* LCH_BASS_BOOST_D1 */
+ case 0x0e: /* LCH_BASS_BOOST_D2 */
+ case 0x0f: /* LCH_BASS_BOOST_D4 */
+ case 0x10: /* LCH_BASS_BOOST_D5 */
+ case 0x11: /* RCH_BASS_BOOST_N0 */
+ case 0x12: /* RCH_BASS_BOOST_N1 */
+ case 0x13: /* RCH_BASS_BOOST_N2 */
+ case 0x14: /* RCH_BASS_BOOST_N3 */
+ case 0x15: /* RCH_BASS_BOOST_N4 */
+ case 0x16: /* RCH_BASS_BOOST_N5 */
+ case 0x17: /* RCH_BASS_BOOST_D1 */
+ case 0x18: /* RCH_BASS_BOOST_D2 */
+ case 0x19: /* RCH_BASS_BOOST_D4 */
+ case 0x1a: /* RCH_BASS_BOOST_D5 */
+ return s->filter_data[reg - 0x07];
+
+ case 0x1b: /* PLL Programmability 1 */
+ return s->pll[0];
+
+ case 0x1c: /* PLL Programmability 2 */
+ return s->pll[1];
+
+ case 0x1d: /* Audio Control 4 */
+ return (!s->softstep) << 14;
+
+ default:
+#ifdef TSC_VERBOSE
+ fprintf(stderr, "tsc2102_audio_register_read: "
+ "no such register: 0x%02x\n", reg);
+#endif
+ return 0xffff;
+ }
+}
+
+static void tsc2102_data_register_write(
+ TSC210xState *s, int reg, uint16_t value)
+{
+ switch (reg) {
+ case 0x00: /* X */
+ case 0x01: /* Y */
+ case 0x02: /* Z1 */
+ case 0x03: /* Z2 */
+ case 0x05: /* BAT1 */
+ case 0x06: /* BAT2 */
+ case 0x07: /* AUX1 */
+ case 0x08: /* AUX2 */
+ case 0x09: /* TEMP1 */
+ case 0x0a: /* TEMP2 */
+ return;
+
+ default:
+#ifdef TSC_VERBOSE
+ fprintf(stderr, "tsc2102_data_register_write: "
+ "no such register: 0x%02x\n", reg);
+#endif
+ }
+}
+
+static void tsc2102_control_register_write(
+ TSC210xState *s, int reg, uint16_t value)
+{
+ switch (reg) {
+ case 0x00: /* TSC ADC */
+ s->host_mode = value >> 15;
+ s->enabled = !(value & 0x4000);
+ if (s->busy && !s->enabled)
+ qemu_del_timer(s->timer);
+ s->busy &= s->enabled;
+ s->nextfunction = (value >> 10) & 0xf;
+ s->nextprecision = (value >> 8) & 3;
+ s->filter = value & 0xff;
+ return;
+
+ case 0x01: /* Status / Keypad Control */
+ if ((s->model & 0xff00) == 0x2100)
+ s->pin_func = value >> 14;
+ else {
+ s->kb.scan = (value >> 14) & 1;
+ s->kb.debounce = (value >> 11) & 7;
+ if (s->kb.intr && s->kb.scan) {
+ s->kb.intr = 0;
+ qemu_irq_raise(s->kbint);
+ }
+ }
+ return;
+
+ case 0x02: /* DAC Control */
+ if ((s->model & 0xff00) == 0x2300) {
+ s->dac_power &= 0x7fff;
+ s->dac_power |= 0x8000 & value;
+ } else
+ goto bad_reg;
+ break;
+
+ case 0x03: /* Reference */
+ s->ref = value & 0x1f;
+ return;
+
+ case 0x04: /* Reset */
+ if (value == 0xbb00) {
+ if (s->busy)
+ qemu_del_timer(s->timer);
+ tsc210x_reset(s);
+#ifdef TSC_VERBOSE
+ } else {
+ fprintf(stderr, "tsc2102_control_register_write: "
+ "wrong value written into RESET\n");
+#endif
+ }
+ return;
+
+ case 0x05: /* Configuration */
+ s->timing = value & 0x3f;
+#ifdef TSC_VERBOSE
+ if (value & ~0x3f)
+ fprintf(stderr, "tsc2102_control_register_write: "
+ "wrong value written into CONFIG\n");
+#endif
+ return;
+
+ case 0x06: /* Secondary configuration */
+ if ((s->model & 0xff00) == 0x2100)
+ goto bad_reg;
+ s->kb.mode = value >> 14;
+ s->pll[2] = value & 0x3ffff;
+ return;
+
+ case 0x10: /* Keypad Mask */
+ if ((s->model & 0xff00) == 0x2100)
+ goto bad_reg;
+ s->kb.mask = value;
+ return;
+
+ default:
+ bad_reg:
+#ifdef TSC_VERBOSE
+ fprintf(stderr, "tsc2102_control_register_write: "
+ "no such register: 0x%02x\n", reg);
+#endif
+ }
+}
+
+static void tsc2102_audio_register_write(
+ TSC210xState *s, int reg, uint16_t value)
+{
+ switch (reg) {
+ case 0x00: /* Audio Control 1 */
+ s->audio_ctrl1 = value & 0x0f3f;
+#ifdef TSC_VERBOSE
+ if ((value & ~0x0f3f) || ((value & 7) != ((value >> 3) & 7)))
+ fprintf(stderr, "tsc2102_audio_register_write: "
+ "wrong value written into Audio 1\n");
+#endif
+ tsc2102_audio_rate_update(s);
+ tsc2102_audio_output_update(s);
+ return;
+
+ case 0x01:
+#ifdef TSC_VERBOSE
+ if (value != 0xff00)
+ fprintf(stderr, "tsc2102_audio_register_write: "
+ "wrong value written into reg 0x01\n");
+#endif
+ return;
+
+ case 0x02: /* DAC Volume Control */
+ s->volume = value;
+ s->volume_change = qemu_get_clock_ns(vm_clock);
+ return;
+
+ case 0x03:
+#ifdef TSC_VERBOSE
+ if (value != 0x8b00)
+ fprintf(stderr, "tsc2102_audio_register_write: "
+ "wrong value written into reg 0x03\n");
+#endif
+ return;
+
+ case 0x04: /* Audio Control 2 */
+ s->audio_ctrl2 = value & 0xf7f2;
+#ifdef TSC_VERBOSE
+ if (value & ~0xf7fd)
+ fprintf(stderr, "tsc2102_audio_register_write: "
+ "wrong value written into Audio 2\n");
+#endif
+ return;
+
+ case 0x05: /* Stereo DAC Power Control */
+ if ((value & ~s->dac_power) & (1 << 10))
+ s->powerdown = qemu_get_clock_ns(vm_clock);
+
+ s->dac_power = value & 0x9543;
+#ifdef TSC_VERBOSE
+ if ((value & ~0x9543) != 0x2aa0)
+ fprintf(stderr, "tsc2102_audio_register_write: "
+ "wrong value written into Power\n");
+#endif
+ tsc2102_audio_rate_update(s);
+ tsc2102_audio_output_update(s);
+ return;
+
+ case 0x06: /* Audio Control 3 */
+ s->audio_ctrl3 &= 0x00c0;
+ s->audio_ctrl3 |= value & 0xf800;
+#ifdef TSC_VERBOSE
+ if (value & ~0xf8c7)
+ fprintf(stderr, "tsc2102_audio_register_write: "
+ "wrong value written into Audio 3\n");
+#endif
+ tsc2102_audio_output_update(s);
+ return;
+
+ case 0x07: /* LCH_BASS_BOOST_N0 */
+ case 0x08: /* LCH_BASS_BOOST_N1 */
+ case 0x09: /* LCH_BASS_BOOST_N2 */
+ case 0x0a: /* LCH_BASS_BOOST_N3 */
+ case 0x0b: /* LCH_BASS_BOOST_N4 */
+ case 0x0c: /* LCH_BASS_BOOST_N5 */
+ case 0x0d: /* LCH_BASS_BOOST_D1 */
+ case 0x0e: /* LCH_BASS_BOOST_D2 */
+ case 0x0f: /* LCH_BASS_BOOST_D4 */
+ case 0x10: /* LCH_BASS_BOOST_D5 */
+ case 0x11: /* RCH_BASS_BOOST_N0 */
+ case 0x12: /* RCH_BASS_BOOST_N1 */
+ case 0x13: /* RCH_BASS_BOOST_N2 */
+ case 0x14: /* RCH_BASS_BOOST_N3 */
+ case 0x15: /* RCH_BASS_BOOST_N4 */
+ case 0x16: /* RCH_BASS_BOOST_N5 */
+ case 0x17: /* RCH_BASS_BOOST_D1 */
+ case 0x18: /* RCH_BASS_BOOST_D2 */
+ case 0x19: /* RCH_BASS_BOOST_D4 */
+ case 0x1a: /* RCH_BASS_BOOST_D5 */
+ s->filter_data[reg - 0x07] = value;
+ return;
+
+ case 0x1b: /* PLL Programmability 1 */
+ s->pll[0] = value & 0xfffc;
+#ifdef TSC_VERBOSE
+ if (value & ~0xfffc)
+ fprintf(stderr, "tsc2102_audio_register_write: "
+ "wrong value written into PLL 1\n");
+#endif
+ return;
+
+ case 0x1c: /* PLL Programmability 2 */
+ s->pll[1] = value & 0xfffc;
+#ifdef TSC_VERBOSE
+ if (value & ~0xfffc)
+ fprintf(stderr, "tsc2102_audio_register_write: "
+ "wrong value written into PLL 2\n");
+#endif
+ return;
+
+ case 0x1d: /* Audio Control 4 */
+ s->softstep = !(value & 0x4000);
+#ifdef TSC_VERBOSE
+ if (value & ~0x4000)
+ fprintf(stderr, "tsc2102_audio_register_write: "
+ "wrong value written into Audio 4\n");
+#endif
+ return;
+
+ default:
+#ifdef TSC_VERBOSE
+ fprintf(stderr, "tsc2102_audio_register_write: "
+ "no such register: 0x%02x\n", reg);
+#endif
+ }
+}
+
+/* This handles most of the chip logic. */
+static void tsc210x_pin_update(TSC210xState *s)
+{
+ int64_t expires;
+ int pin_state;
+
+ switch (s->pin_func) {
+ case 0:
+ pin_state = s->pressure;
+ break;
+ case 1:
+ pin_state = !!s->dav;
+ break;
+ case 2:
+ default:
+ pin_state = s->pressure && !s->dav;
+ }
+
+ if (!s->enabled)
+ pin_state = 0;
+
+ if (pin_state != s->irq) {
+ s->irq = pin_state;
+ qemu_set_irq(s->pint, !s->irq);
+ }
+
+ switch (s->nextfunction) {
+ case TSC_MODE_XY_SCAN:
+ case TSC_MODE_XYZ_SCAN:
+ if (!s->pressure)
+ return;
+ break;
+
+ case TSC_MODE_X:
+ case TSC_MODE_Y:
+ case TSC_MODE_Z:
+ if (!s->pressure)
+ return;
+ /* Fall through */
+ case TSC_MODE_BAT1:
+ case TSC_MODE_BAT2:
+ case TSC_MODE_AUX:
+ case TSC_MODE_TEMP1:
+ case TSC_MODE_TEMP2:
+ if (s->dav)
+ s->enabled = 0;
+ break;
+
+ case TSC_MODE_AUX_SCAN:
+ case TSC_MODE_PORT_SCAN:
+ break;
+
+ case TSC_MODE_NO_SCAN:
+ case TSC_MODE_XX_DRV:
+ case TSC_MODE_YY_DRV:
+ case TSC_MODE_YX_DRV:
+ default:
+ return;
+ }
+
+ if (!s->enabled || s->busy || s->dav)
+ return;
+
+ s->busy = 1;
+ s->precision = s->nextprecision;
+ s->function = s->nextfunction;
+ expires = qemu_get_clock_ns(vm_clock) + (get_ticks_per_sec() >> 10);
+ qemu_mod_timer(s->timer, expires);
+}
+
+static uint16_t tsc210x_read(TSC210xState *s)
+{
+ uint16_t ret = 0x0000;
+
+ if (!s->command)
+ fprintf(stderr, "tsc210x_read: SPI underrun!\n");
+
+ switch (s->page) {
+ case TSC_DATA_REGISTERS_PAGE:
+ ret = tsc2102_data_register_read(s, s->offset);
+ if (!s->dav)
+ qemu_irq_raise(s->davint);
+ break;
+ case TSC_CONTROL_REGISTERS_PAGE:
+ ret = tsc2102_control_register_read(s, s->offset);
+ break;
+ case TSC_AUDIO_REGISTERS_PAGE:
+ ret = tsc2102_audio_register_read(s, s->offset);
+ break;
+ default:
+ hw_error("tsc210x_read: wrong memory page\n");
+ }
+
+ tsc210x_pin_update(s);
+
+ /* Allow sequential reads. */
+ s->offset ++;
+ s->state = 0;
+ return ret;
+}
+
+static void tsc210x_write(TSC210xState *s, uint16_t value)
+{
+ /*
+ * This is a two-state state machine for reading
+ * command and data every second time.
+ */
+ if (!s->state) {
+ s->command = value >> 15;
+ s->page = (value >> 11) & 0x0f;
+ s->offset = (value >> 5) & 0x3f;
+ s->state = 1;
+ } else {
+ if (s->command)
+ fprintf(stderr, "tsc210x_write: SPI overrun!\n");
+ else
+ switch (s->page) {
+ case TSC_DATA_REGISTERS_PAGE:
+ tsc2102_data_register_write(s, s->offset, value);
+ break;
+ case TSC_CONTROL_REGISTERS_PAGE:
+ tsc2102_control_register_write(s, s->offset, value);
+ break;
+ case TSC_AUDIO_REGISTERS_PAGE:
+ tsc2102_audio_register_write(s, s->offset, value);
+ break;
+ default:
+ hw_error("tsc210x_write: wrong memory page\n");
+ }
+
+ tsc210x_pin_update(s);
+ s->state = 0;
+ }
+}
+
+uint32_t tsc210x_txrx(void *opaque, uint32_t value, int len)
+{
+ TSC210xState *s = opaque;
+ uint32_t ret = 0;
+
+ if (len != 16)
+ hw_error("%s: FIXME: bad SPI word width %i\n", __FUNCTION__, len);
+
+ /* TODO: sequential reads etc - how do we make sure the host doesn't
+ * unintentionally read out a conversion result from a register while
+ * transmitting the command word of the next command? */
+ if (!value || (s->state && s->command))
+ ret = tsc210x_read(s);
+ if (value || (s->state && !s->command))
+ tsc210x_write(s, value);
+
+ return ret;
+}
+
+static void tsc210x_timer_tick(void *opaque)
+{
+ TSC210xState *s = opaque;
+
+ /* Timer ticked -- a set of conversions has been finished. */
+
+ if (!s->busy)
+ return;
+
+ s->busy = 0;
+ s->dav |= mode_regs[s->function];
+ tsc210x_pin_update(s);
+ qemu_irq_lower(s->davint);
+}
+
+static void tsc210x_touchscreen_event(void *opaque,
+ int x, int y, int z, int buttons_state)
+{
+ TSC210xState *s = opaque;
+ int p = s->pressure;
+
+ if (buttons_state) {
+ s->x = x;
+ s->y = y;
+ }
+ s->pressure = !!buttons_state;
+
+ /*
+ * Note: We would get better responsiveness in the guest by
+ * signaling TS events immediately, but for now we simulate
+ * the first conversion delay for sake of correctness.
+ */
+ if (p != s->pressure)
+ tsc210x_pin_update(s);
+}
+
+static void tsc210x_i2s_swallow(TSC210xState *s)
+{
+ if (s->dac_voice[0])
+ tsc210x_out_flush(s, s->codec.out.len);
+ else
+ s->codec.out.len = 0;
+}
+
+static void tsc210x_i2s_set_rate(TSC210xState *s, int in, int out)
+{
+ s->i2s_tx_rate = out;
+ s->i2s_rx_rate = in;
+}
+
+static void tsc210x_save(QEMUFile *f, void *opaque)
+{
+ TSC210xState *s = (TSC210xState *) opaque;
+ int64_t now = qemu_get_clock_ns(vm_clock);
+ int i;
+
+ qemu_put_be16(f, s->x);
+ qemu_put_be16(f, s->y);
+ qemu_put_byte(f, s->pressure);
+
+ qemu_put_byte(f, s->state);
+ qemu_put_byte(f, s->page);
+ qemu_put_byte(f, s->offset);
+ qemu_put_byte(f, s->command);
+
+ qemu_put_byte(f, s->irq);
+ qemu_put_be16s(f, &s->dav);
+
+ qemu_put_timer(f, s->timer);
+ qemu_put_byte(f, s->enabled);
+ qemu_put_byte(f, s->host_mode);
+ qemu_put_byte(f, s->function);
+ qemu_put_byte(f, s->nextfunction);
+ qemu_put_byte(f, s->precision);
+ qemu_put_byte(f, s->nextprecision);
+ qemu_put_byte(f, s->filter);
+ qemu_put_byte(f, s->pin_func);
+ qemu_put_byte(f, s->ref);
+ qemu_put_byte(f, s->timing);
+ qemu_put_be32(f, s->noise);
+
+ qemu_put_be16s(f, &s->audio_ctrl1);
+ qemu_put_be16s(f, &s->audio_ctrl2);
+ qemu_put_be16s(f, &s->audio_ctrl3);
+ qemu_put_be16s(f, &s->pll[0]);
+ qemu_put_be16s(f, &s->pll[1]);
+ qemu_put_be16s(f, &s->volume);
+ qemu_put_sbe64(f, (s->volume_change - now));
+ qemu_put_sbe64(f, (s->powerdown - now));
+ qemu_put_byte(f, s->softstep);
+ qemu_put_be16s(f, &s->dac_power);
+
+ for (i = 0; i < 0x14; i ++)
+ qemu_put_be16s(f, &s->filter_data[i]);
+}
+
+static int tsc210x_load(QEMUFile *f, void *opaque, int version_id)
+{
+ TSC210xState *s = (TSC210xState *) opaque;
+ int64_t now = qemu_get_clock_ns(vm_clock);
+ int i;
+
+ s->x = qemu_get_be16(f);
+ s->y = qemu_get_be16(f);
+ s->pressure = qemu_get_byte(f);
+
+ s->state = qemu_get_byte(f);
+ s->page = qemu_get_byte(f);
+ s->offset = qemu_get_byte(f);
+ s->command = qemu_get_byte(f);
+
+ s->irq = qemu_get_byte(f);
+ qemu_get_be16s(f, &s->dav);
+
+ qemu_get_timer(f, s->timer);
+ s->enabled = qemu_get_byte(f);
+ s->host_mode = qemu_get_byte(f);
+ s->function = qemu_get_byte(f);
+ s->nextfunction = qemu_get_byte(f);
+ s->precision = qemu_get_byte(f);
+ s->nextprecision = qemu_get_byte(f);
+ s->filter = qemu_get_byte(f);
+ s->pin_func = qemu_get_byte(f);
+ s->ref = qemu_get_byte(f);
+ s->timing = qemu_get_byte(f);
+ s->noise = qemu_get_be32(f);
+
+ qemu_get_be16s(f, &s->audio_ctrl1);
+ qemu_get_be16s(f, &s->audio_ctrl2);
+ qemu_get_be16s(f, &s->audio_ctrl3);
+ qemu_get_be16s(f, &s->pll[0]);
+ qemu_get_be16s(f, &s->pll[1]);
+ qemu_get_be16s(f, &s->volume);
+ s->volume_change = qemu_get_sbe64(f) + now;
+ s->powerdown = qemu_get_sbe64(f) + now;
+ s->softstep = qemu_get_byte(f);
+ qemu_get_be16s(f, &s->dac_power);
+
+ for (i = 0; i < 0x14; i ++)
+ qemu_get_be16s(f, &s->filter_data[i]);
+
+ s->busy = qemu_timer_pending(s->timer);
+ qemu_set_irq(s->pint, !s->irq);
+ qemu_set_irq(s->davint, !s->dav);
+
+ return 0;
+}
+
+uWireSlave *tsc2102_init(qemu_irq pint)
+{
+ TSC210xState *s;
+
+ s = (TSC210xState *)
+ g_malloc0(sizeof(TSC210xState));
+ memset(s, 0, sizeof(TSC210xState));
+ s->x = 160;
+ s->y = 160;
+ s->pressure = 0;
+ s->precision = s->nextprecision = 0;
+ s->timer = qemu_new_timer_ns(vm_clock, tsc210x_timer_tick, s);
+ s->pint = pint;
+ s->model = 0x2102;
+ s->name = "tsc2102";
+
+ s->tr[0] = 0;
+ s->tr[1] = 1;
+ s->tr[2] = 1;
+ s->tr[3] = 0;
+ s->tr[4] = 1;
+ s->tr[5] = 0;
+ s->tr[6] = 1;
+ s->tr[7] = 0;
+
+ s->chip.opaque = s;
+ s->chip.send = (void *) tsc210x_write;
+ s->chip.receive = (void *) tsc210x_read;
+
+ s->codec.opaque = s;
+ s->codec.tx_swallow = (void *) tsc210x_i2s_swallow;
+ s->codec.set_rate = (void *) tsc210x_i2s_set_rate;
+ s->codec.in.fifo = s->in_fifo;
+ s->codec.out.fifo = s->out_fifo;
+
+ tsc210x_reset(s);
+
+ qemu_add_mouse_event_handler(tsc210x_touchscreen_event, s, 1,
+ "QEMU TSC2102-driven Touchscreen");
+
+ AUD_register_card(s->name, &s->card);
+
+ qemu_register_reset((void *) tsc210x_reset, s);
+ register_savevm(NULL, s->name, -1, 0,
+ tsc210x_save, tsc210x_load, s);
+
+ return &s->chip;
+}
+
+uWireSlave *tsc2301_init(qemu_irq penirq, qemu_irq kbirq, qemu_irq dav)
+{
+ TSC210xState *s;
+
+ s = (TSC210xState *)
+ g_malloc0(sizeof(TSC210xState));
+ memset(s, 0, sizeof(TSC210xState));
+ s->x = 400;
+ s->y = 240;
+ s->pressure = 0;
+ s->precision = s->nextprecision = 0;
+ s->timer = qemu_new_timer_ns(vm_clock, tsc210x_timer_tick, s);
+ s->pint = penirq;
+ s->kbint = kbirq;
+ s->davint = dav;
+ s->model = 0x2301;
+ s->name = "tsc2301";
+
+ s->tr[0] = 0;
+ s->tr[1] = 1;
+ s->tr[2] = 1;
+ s->tr[3] = 0;
+ s->tr[4] = 1;
+ s->tr[5] = 0;
+ s->tr[6] = 1;
+ s->tr[7] = 0;
+
+ s->chip.opaque = s;
+ s->chip.send = (void *) tsc210x_write;
+ s->chip.receive = (void *) tsc210x_read;
+
+ s->codec.opaque = s;
+ s->codec.tx_swallow = (void *) tsc210x_i2s_swallow;
+ s->codec.set_rate = (void *) tsc210x_i2s_set_rate;
+ s->codec.in.fifo = s->in_fifo;
+ s->codec.out.fifo = s->out_fifo;
+
+ tsc210x_reset(s);
+
+ qemu_add_mouse_event_handler(tsc210x_touchscreen_event, s, 1,
+ "QEMU TSC2301-driven Touchscreen");
+
+ AUD_register_card(s->name, &s->card);
+
+ qemu_register_reset((void *) tsc210x_reset, s);
+ register_savevm(NULL, s->name, -1, 0, tsc210x_save, tsc210x_load, s);
+
+ return &s->chip;
+}
+
+I2SCodec *tsc210x_codec(uWireSlave *chip)
+{
+ TSC210xState *s = (TSC210xState *) chip->opaque;
+
+ return &s->codec;
+}
+
+/*
+ * Use tslib generated calibration data to generate ADC input values
+ * from the touchscreen. Assuming 12-bit precision was used during
+ * tslib calibration.
+ */
+void tsc210x_set_transform(uWireSlave *chip,
+ MouseTransformInfo *info)
+{
+ TSC210xState *s = (TSC210xState *) chip->opaque;
+#if 0
+ int64_t ltr[8];
+
+ ltr[0] = (int64_t) info->a[1] * info->y;
+ ltr[1] = (int64_t) info->a[4] * info->x;
+ ltr[2] = (int64_t) info->a[1] * info->a[3] -
+ (int64_t) info->a[4] * info->a[0];
+ ltr[3] = (int64_t) info->a[2] * info->a[4] -
+ (int64_t) info->a[5] * info->a[1];
+ ltr[4] = (int64_t) info->a[0] * info->y;
+ ltr[5] = (int64_t) info->a[3] * info->x;
+ ltr[6] = (int64_t) info->a[4] * info->a[0] -
+ (int64_t) info->a[1] * info->a[3];
+ ltr[7] = (int64_t) info->a[2] * info->a[3] -
+ (int64_t) info->a[5] * info->a[0];
+
+ /* Avoid integer overflow */
+ s->tr[0] = ltr[0] >> 11;
+ s->tr[1] = ltr[1] >> 11;
+ s->tr[2] = muldiv64(ltr[2], 1, info->a[6]);
+ s->tr[3] = muldiv64(ltr[3], 1 << 4, ltr[2]);
+ s->tr[4] = ltr[4] >> 11;
+ s->tr[5] = ltr[5] >> 11;
+ s->tr[6] = muldiv64(ltr[6], 1, info->a[6]);
+ s->tr[7] = muldiv64(ltr[7], 1 << 4, ltr[6]);
+#else
+
+ /* This version assumes touchscreen X & Y axis are parallel or
+ * perpendicular to LCD's X & Y axis in some way. */
+ if (abs(info->a[0]) > abs(info->a[1])) {
+ s->tr[0] = 0;
+ s->tr[1] = -info->a[6] * info->x;
+ s->tr[2] = info->a[0];
+ s->tr[3] = -info->a[2] / info->a[0];
+ s->tr[4] = info->a[6] * info->y;
+ s->tr[5] = 0;
+ s->tr[6] = info->a[4];
+ s->tr[7] = -info->a[5] / info->a[4];
+ } else {
+ s->tr[0] = info->a[6] * info->y;
+ s->tr[1] = 0;
+ s->tr[2] = info->a[1];
+ s->tr[3] = -info->a[2] / info->a[1];
+ s->tr[4] = 0;
+ s->tr[5] = -info->a[6] * info->x;
+ s->tr[6] = info->a[3];
+ s->tr[7] = -info->a[5] / info->a[3];
+ }
+
+ s->tr[0] >>= 11;
+ s->tr[1] >>= 11;
+ s->tr[3] <<= 4;
+ s->tr[4] >>= 11;
+ s->tr[5] >>= 11;
+ s->tr[7] <<= 4;
+#endif
+}
+
+void tsc210x_key_event(uWireSlave *chip, int key, int down)
+{
+ TSC210xState *s = (TSC210xState *) chip->opaque;
+
+ if (down)
+ s->kb.down |= 1 << key;
+ else
+ s->kb.down &= ~(1 << key);
+
+ if (down && (s->kb.down & ~s->kb.mask) && !s->kb.intr) {
+ s->kb.intr = 1;
+ qemu_irq_lower(s->kbint);
+ } else if (s->kb.intr && !(s->kb.down & ~s->kb.mask) &&
+ !(s->kb.mode & 1)) {
+ s->kb.intr = 0;
+ qemu_irq_raise(s->kbint);
+ }
+}
# LM32 peripherals
obj-y += lm32_pic.o
-obj-y += lm32_timer.o
obj-y += lm32_sys.o
obj-y += milkymist-hpdmc.o
obj-y += milkymist-memcard.o
obj-y += milkymist-pfpu.o
-obj-y += milkymist-softusb.o
-obj-y += milkymist-sysctl.o
obj-y := $(addprefix ../,$(obj-y))
+++ /dev/null
-/*
- * QEMU model of the LatticeMico32 timer block.
- *
- * Copyright (c) 2010 Michael Walle <michael@walle.cc>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
- *
- *
- * Specification available at:
- * http://www.latticesemi.com/documents/mico32timer.pdf
- */
-
-#include "hw/hw.h"
-#include "hw/sysbus.h"
-#include "trace.h"
-#include "qemu/timer.h"
-#include "hw/ptimer.h"
-#include "qemu/error-report.h"
-
-#define DEFAULT_FREQUENCY (50*1000000)
-
-enum {
- R_SR = 0,
- R_CR,
- R_PERIOD,
- R_SNAPSHOT,
- R_MAX
-};
-
-enum {
- SR_TO = (1 << 0),
- SR_RUN = (1 << 1),
-};
-
-enum {
- CR_ITO = (1 << 0),
- CR_CONT = (1 << 1),
- CR_START = (1 << 2),
- CR_STOP = (1 << 3),
-};
-
-struct LM32TimerState {
- SysBusDevice busdev;
- MemoryRegion iomem;
-
- QEMUBH *bh;
- ptimer_state *ptimer;
-
- qemu_irq irq;
- uint32_t freq_hz;
-
- uint32_t regs[R_MAX];
-};
-typedef struct LM32TimerState LM32TimerState;
-
-static void timer_update_irq(LM32TimerState *s)
-{
- int state = (s->regs[R_SR] & SR_TO) && (s->regs[R_CR] & CR_ITO);
-
- trace_lm32_timer_irq_state(state);
- qemu_set_irq(s->irq, state);
-}
-
-static uint64_t timer_read(void *opaque, hwaddr addr, unsigned size)
-{
- LM32TimerState *s = opaque;
- uint32_t r = 0;
-
- addr >>= 2;
- switch (addr) {
- case R_SR:
- case R_CR:
- case R_PERIOD:
- r = s->regs[addr];
- break;
- case R_SNAPSHOT:
- r = (uint32_t)ptimer_get_count(s->ptimer);
- break;
- default:
- error_report("lm32_timer: read access to unknown register 0x"
- TARGET_FMT_plx, addr << 2);
- break;
- }
-
- trace_lm32_timer_memory_read(addr << 2, r);
- return r;
-}
-
-static void timer_write(void *opaque, hwaddr addr,
- uint64_t value, unsigned size)
-{
- LM32TimerState *s = opaque;
-
- trace_lm32_timer_memory_write(addr, value);
-
- addr >>= 2;
- switch (addr) {
- case R_SR:
- s->regs[R_SR] &= ~SR_TO;
- break;
- case R_CR:
- s->regs[R_CR] = value;
- if (s->regs[R_CR] & CR_START) {
- ptimer_run(s->ptimer, 1);
- }
- if (s->regs[R_CR] & CR_STOP) {
- ptimer_stop(s->ptimer);
- }
- break;
- case R_PERIOD:
- s->regs[R_PERIOD] = value;
- ptimer_set_count(s->ptimer, value);
- break;
- case R_SNAPSHOT:
- error_report("lm32_timer: write access to read only register 0x"
- TARGET_FMT_plx, addr << 2);
- break;
- default:
- error_report("lm32_timer: write access to unknown register 0x"
- TARGET_FMT_plx, addr << 2);
- break;
- }
- timer_update_irq(s);
-}
-
-static const MemoryRegionOps timer_ops = {
- .read = timer_read,
- .write = timer_write,
- .endianness = DEVICE_NATIVE_ENDIAN,
- .valid = {
- .min_access_size = 4,
- .max_access_size = 4,
- },
-};
-
-static void timer_hit(void *opaque)
-{
- LM32TimerState *s = opaque;
-
- trace_lm32_timer_hit();
-
- s->regs[R_SR] |= SR_TO;
-
- if (s->regs[R_CR] & CR_CONT) {
- ptimer_set_count(s->ptimer, s->regs[R_PERIOD]);
- ptimer_run(s->ptimer, 1);
- }
- timer_update_irq(s);
-}
-
-static void timer_reset(DeviceState *d)
-{
- LM32TimerState *s = container_of(d, LM32TimerState, busdev.qdev);
- int i;
-
- for (i = 0; i < R_MAX; i++) {
- s->regs[i] = 0;
- }
- ptimer_stop(s->ptimer);
-}
-
-static int lm32_timer_init(SysBusDevice *dev)
-{
- LM32TimerState *s = FROM_SYSBUS(typeof(*s), dev);
-
- sysbus_init_irq(dev, &s->irq);
-
- s->bh = qemu_bh_new(timer_hit, s);
- s->ptimer = ptimer_init(s->bh);
- ptimer_set_freq(s->ptimer, s->freq_hz);
-
- memory_region_init_io(&s->iomem, &timer_ops, s, "timer", R_MAX * 4);
- sysbus_init_mmio(dev, &s->iomem);
-
- return 0;
-}
-
-static const VMStateDescription vmstate_lm32_timer = {
- .name = "lm32-timer",
- .version_id = 1,
- .minimum_version_id = 1,
- .minimum_version_id_old = 1,
- .fields = (VMStateField[]) {
- VMSTATE_PTIMER(ptimer, LM32TimerState),
- VMSTATE_UINT32(freq_hz, LM32TimerState),
- VMSTATE_UINT32_ARRAY(regs, LM32TimerState, R_MAX),
- VMSTATE_END_OF_LIST()
- }
-};
-
-static Property lm32_timer_properties[] = {
- DEFINE_PROP_UINT32("frequency", LM32TimerState, freq_hz, DEFAULT_FREQUENCY),
- DEFINE_PROP_END_OF_LIST(),
-};
-
-static void lm32_timer_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-
- k->init = lm32_timer_init;
- dc->reset = timer_reset;
- dc->vmsd = &vmstate_lm32_timer;
- dc->props = lm32_timer_properties;
-}
-
-static const TypeInfo lm32_timer_info = {
- .name = "lm32-timer",
- .parent = TYPE_SYS_BUS_DEVICE,
- .instance_size = sizeof(LM32TimerState),
- .class_init = lm32_timer_class_init,
-};
-
-static void lm32_timer_register_types(void)
-{
- type_register_static(&lm32_timer_info);
-}
-
-type_init(lm32_timer_register_types)
+++ /dev/null
-/*
- * QEMU model of the Milkymist SoftUSB block.
- *
- * Copyright (c) 2010 Michael Walle <michael@walle.cc>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
- *
- *
- * Specification available at:
- * not available yet
- */
-
-#include "hw/hw.h"
-#include "hw/sysbus.h"
-#include "trace.h"
-#include "ui/console.h"
-#include "hw/input/hid.h"
-#include "qemu/error-report.h"
-
-enum {
- R_CTRL = 0,
- R_MAX
-};
-
-enum {
- CTRL_RESET = (1<<0),
-};
-
-#define COMLOC_DEBUG_PRODUCE 0x1000
-#define COMLOC_DEBUG_BASE 0x1001
-#define COMLOC_MEVT_PRODUCE 0x1101
-#define COMLOC_MEVT_BASE 0x1102
-#define COMLOC_KEVT_PRODUCE 0x1142
-#define COMLOC_KEVT_BASE 0x1143
-
-struct MilkymistSoftUsbState {
- SysBusDevice busdev;
- HIDState hid_kbd;
- HIDState hid_mouse;
-
- MemoryRegion regs_region;
- MemoryRegion pmem;
- MemoryRegion dmem;
- qemu_irq irq;
-
- void *pmem_ptr;
- void *dmem_ptr;
-
- /* device properties */
- uint32_t pmem_size;
- uint32_t dmem_size;
-
- /* device registers */
- uint32_t regs[R_MAX];
-
- /* mouse state */
- uint8_t mouse_hid_buffer[4];
-
- /* keyboard state */
- uint8_t kbd_hid_buffer[8];
-};
-typedef struct MilkymistSoftUsbState MilkymistSoftUsbState;
-
-static uint64_t softusb_read(void *opaque, hwaddr addr,
- unsigned size)
-{
- MilkymistSoftUsbState *s = opaque;
- uint32_t r = 0;
-
- addr >>= 2;
- switch (addr) {
- case R_CTRL:
- r = s->regs[addr];
- break;
-
- default:
- error_report("milkymist_softusb: read access to unknown register 0x"
- TARGET_FMT_plx, addr << 2);
- break;
- }
-
- trace_milkymist_softusb_memory_read(addr << 2, r);
-
- return r;
-}
-
-static void
-softusb_write(void *opaque, hwaddr addr, uint64_t value,
- unsigned size)
-{
- MilkymistSoftUsbState *s = opaque;
-
- trace_milkymist_softusb_memory_write(addr, value);
-
- addr >>= 2;
- switch (addr) {
- case R_CTRL:
- s->regs[addr] = value;
- break;
-
- default:
- error_report("milkymist_softusb: write access to unknown register 0x"
- TARGET_FMT_plx, addr << 2);
- break;
- }
-}
-
-static const MemoryRegionOps softusb_mmio_ops = {
- .read = softusb_read,
- .write = softusb_write,
- .endianness = DEVICE_NATIVE_ENDIAN,
- .valid = {
- .min_access_size = 4,
- .max_access_size = 4,
- },
-};
-
-static inline void softusb_read_dmem(MilkymistSoftUsbState *s,
- uint32_t offset, uint8_t *buf, uint32_t len)
-{
- if (offset + len >= s->dmem_size) {
- error_report("milkymist_softusb: read dmem out of bounds "
- "at offset 0x%x, len %d", offset, len);
- memset(buf, 0, len);
- return;
- }
-
- memcpy(buf, s->dmem_ptr + offset, len);
-}
-
-static inline void softusb_write_dmem(MilkymistSoftUsbState *s,
- uint32_t offset, uint8_t *buf, uint32_t len)
-{
- if (offset + len >= s->dmem_size) {
- error_report("milkymist_softusb: write dmem out of bounds "
- "at offset 0x%x, len %d", offset, len);
- return;
- }
-
- memcpy(s->dmem_ptr + offset, buf, len);
-}
-
-static inline void softusb_read_pmem(MilkymistSoftUsbState *s,
- uint32_t offset, uint8_t *buf, uint32_t len)
-{
- if (offset + len >= s->pmem_size) {
- error_report("milkymist_softusb: read pmem out of bounds "
- "at offset 0x%x, len %d", offset, len);
- memset(buf, 0, len);
- return;
- }
-
- memcpy(buf, s->pmem_ptr + offset, len);
-}
-
-static inline void softusb_write_pmem(MilkymistSoftUsbState *s,
- uint32_t offset, uint8_t *buf, uint32_t len)
-{
- if (offset + len >= s->pmem_size) {
- error_report("milkymist_softusb: write pmem out of bounds "
- "at offset 0x%x, len %d", offset, len);
- return;
- }
-
- memcpy(s->pmem_ptr + offset, buf, len);
-}
-
-static void softusb_mouse_changed(MilkymistSoftUsbState *s)
-{
- uint8_t m;
-
- softusb_read_dmem(s, COMLOC_MEVT_PRODUCE, &m, 1);
- trace_milkymist_softusb_mevt(m);
- softusb_write_dmem(s, COMLOC_MEVT_BASE + 4 * m, s->mouse_hid_buffer, 4);
- m = (m + 1) & 0xf;
- softusb_write_dmem(s, COMLOC_MEVT_PRODUCE, &m, 1);
-
- trace_milkymist_softusb_pulse_irq();
- qemu_irq_pulse(s->irq);
-}
-
-static void softusb_kbd_changed(MilkymistSoftUsbState *s)
-{
- uint8_t m;
-
- softusb_read_dmem(s, COMLOC_KEVT_PRODUCE, &m, 1);
- trace_milkymist_softusb_kevt(m);
- softusb_write_dmem(s, COMLOC_KEVT_BASE + 8 * m, s->kbd_hid_buffer, 8);
- m = (m + 1) & 0x7;
- softusb_write_dmem(s, COMLOC_KEVT_PRODUCE, &m, 1);
-
- trace_milkymist_softusb_pulse_irq();
- qemu_irq_pulse(s->irq);
-}
-
-static void softusb_kbd_hid_datain(HIDState *hs)
-{
- MilkymistSoftUsbState *s = container_of(hs, MilkymistSoftUsbState, hid_kbd);
- int len;
-
- /* if device is in reset, do nothing */
- if (s->regs[R_CTRL] & CTRL_RESET) {
- return;
- }
-
- len = hid_keyboard_poll(hs, s->kbd_hid_buffer, sizeof(s->kbd_hid_buffer));
-
- if (len == 8) {
- softusb_kbd_changed(s);
- }
-}
-
-static void softusb_mouse_hid_datain(HIDState *hs)
-{
- MilkymistSoftUsbState *s =
- container_of(hs, MilkymistSoftUsbState, hid_mouse);
- int len;
-
- /* if device is in reset, do nothing */
- if (s->regs[R_CTRL] & CTRL_RESET) {
- return;
- }
-
- len = hid_pointer_poll(hs, s->mouse_hid_buffer,
- sizeof(s->mouse_hid_buffer));
-
- if (len == 4) {
- softusb_mouse_changed(s);
- }
-}
-
-static void milkymist_softusb_reset(DeviceState *d)
-{
- MilkymistSoftUsbState *s =
- container_of(d, MilkymistSoftUsbState, busdev.qdev);
- int i;
-
- for (i = 0; i < R_MAX; i++) {
- s->regs[i] = 0;
- }
- memset(s->kbd_hid_buffer, 0, sizeof(s->kbd_hid_buffer));
- memset(s->mouse_hid_buffer, 0, sizeof(s->mouse_hid_buffer));
-
- hid_reset(&s->hid_kbd);
- hid_reset(&s->hid_mouse);
-
- /* defaults */
- s->regs[R_CTRL] = CTRL_RESET;
-}
-
-static int milkymist_softusb_init(SysBusDevice *dev)
-{
- MilkymistSoftUsbState *s = FROM_SYSBUS(typeof(*s), dev);
-
- sysbus_init_irq(dev, &s->irq);
-
- memory_region_init_io(&s->regs_region, &softusb_mmio_ops, s,
- "milkymist-softusb", R_MAX * 4);
- sysbus_init_mmio(dev, &s->regs_region);
-
- /* register pmem and dmem */
- memory_region_init_ram(&s->pmem, "milkymist-softusb.pmem",
- s->pmem_size);
- vmstate_register_ram_global(&s->pmem);
- s->pmem_ptr = memory_region_get_ram_ptr(&s->pmem);
- sysbus_init_mmio(dev, &s->pmem);
- memory_region_init_ram(&s->dmem, "milkymist-softusb.dmem",
- s->dmem_size);
- vmstate_register_ram_global(&s->dmem);
- s->dmem_ptr = memory_region_get_ram_ptr(&s->dmem);
- sysbus_init_mmio(dev, &s->dmem);
-
- hid_init(&s->hid_kbd, HID_KEYBOARD, softusb_kbd_hid_datain);
- hid_init(&s->hid_mouse, HID_MOUSE, softusb_mouse_hid_datain);
-
- return 0;
-}
-
-static const VMStateDescription vmstate_milkymist_softusb = {
- .name = "milkymist-softusb",
- .version_id = 1,
- .minimum_version_id = 1,
- .minimum_version_id_old = 1,
- .fields = (VMStateField[]) {
- VMSTATE_UINT32_ARRAY(regs, MilkymistSoftUsbState, R_MAX),
- VMSTATE_HID_KEYBOARD_DEVICE(hid_kbd, MilkymistSoftUsbState),
- VMSTATE_HID_POINTER_DEVICE(hid_mouse, MilkymistSoftUsbState),
- VMSTATE_BUFFER(kbd_hid_buffer, MilkymistSoftUsbState),
- VMSTATE_BUFFER(mouse_hid_buffer, MilkymistSoftUsbState),
- VMSTATE_END_OF_LIST()
- }
-};
-
-static Property milkymist_softusb_properties[] = {
- DEFINE_PROP_UINT32("pmem_size", MilkymistSoftUsbState, pmem_size, 0x00001000),
- DEFINE_PROP_UINT32("dmem_size", MilkymistSoftUsbState, dmem_size, 0x00002000),
- DEFINE_PROP_END_OF_LIST(),
-};
-
-static void milkymist_softusb_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-
- k->init = milkymist_softusb_init;
- dc->reset = milkymist_softusb_reset;
- dc->vmsd = &vmstate_milkymist_softusb;
- dc->props = milkymist_softusb_properties;
-}
-
-static const TypeInfo milkymist_softusb_info = {
- .name = "milkymist-softusb",
- .parent = TYPE_SYS_BUS_DEVICE,
- .instance_size = sizeof(MilkymistSoftUsbState),
- .class_init = milkymist_softusb_class_init,
-};
-
-static void milkymist_softusb_register_types(void)
-{
- type_register_static(&milkymist_softusb_info);
-}
-
-type_init(milkymist_softusb_register_types)
+++ /dev/null
-/*
- * QEMU model of the Milkymist System Controller.
- *
- * Copyright (c) 2010-2012 Michael Walle <michael@walle.cc>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
- *
- *
- * Specification available at:
- * http://www.milkymist.org/socdoc/sysctl.pdf
- */
-
-#include "hw/hw.h"
-#include "hw/sysbus.h"
-#include "sysemu/sysemu.h"
-#include "trace.h"
-#include "qemu/timer.h"
-#include "hw/ptimer.h"
-#include "qemu/error-report.h"
-
-enum {
- CTRL_ENABLE = (1<<0),
- CTRL_AUTORESTART = (1<<1),
-};
-
-enum {
- ICAP_READY = (1<<0),
-};
-
-enum {
- R_GPIO_IN = 0,
- R_GPIO_OUT,
- R_GPIO_INTEN,
- R_TIMER0_CONTROL = 4,
- R_TIMER0_COMPARE,
- R_TIMER0_COUNTER,
- R_TIMER1_CONTROL = 8,
- R_TIMER1_COMPARE,
- R_TIMER1_COUNTER,
- R_ICAP = 16,
- R_DBG_SCRATCHPAD = 20,
- R_DBG_WRITE_LOCK,
- R_CLK_FREQUENCY = 29,
- R_CAPABILITIES,
- R_SYSTEM_ID,
- R_MAX
-};
-
-struct MilkymistSysctlState {
- SysBusDevice busdev;
- MemoryRegion regs_region;
-
- QEMUBH *bh0;
- QEMUBH *bh1;
- ptimer_state *ptimer0;
- ptimer_state *ptimer1;
-
- uint32_t freq_hz;
- uint32_t capabilities;
- uint32_t systemid;
- uint32_t strappings;
-
- uint32_t regs[R_MAX];
-
- qemu_irq gpio_irq;
- qemu_irq timer0_irq;
- qemu_irq timer1_irq;
-};
-typedef struct MilkymistSysctlState MilkymistSysctlState;
-
-static void sysctl_icap_write(MilkymistSysctlState *s, uint32_t value)
-{
- trace_milkymist_sysctl_icap_write(value);
- switch (value & 0xffff) {
- case 0x000e:
- qemu_system_shutdown_request();
- break;
- }
-}
-
-static uint64_t sysctl_read(void *opaque, hwaddr addr,
- unsigned size)
-{
- MilkymistSysctlState *s = opaque;
- uint32_t r = 0;
-
- addr >>= 2;
- switch (addr) {
- case R_TIMER0_COUNTER:
- r = (uint32_t)ptimer_get_count(s->ptimer0);
- /* milkymist timer counts up */
- r = s->regs[R_TIMER0_COMPARE] - r;
- break;
- case R_TIMER1_COUNTER:
- r = (uint32_t)ptimer_get_count(s->ptimer1);
- /* milkymist timer counts up */
- r = s->regs[R_TIMER1_COMPARE] - r;
- break;
- case R_GPIO_IN:
- case R_GPIO_OUT:
- case R_GPIO_INTEN:
- case R_TIMER0_CONTROL:
- case R_TIMER0_COMPARE:
- case R_TIMER1_CONTROL:
- case R_TIMER1_COMPARE:
- case R_ICAP:
- case R_DBG_SCRATCHPAD:
- case R_DBG_WRITE_LOCK:
- case R_CLK_FREQUENCY:
- case R_CAPABILITIES:
- case R_SYSTEM_ID:
- r = s->regs[addr];
- break;
-
- default:
- error_report("milkymist_sysctl: read access to unknown register 0x"
- TARGET_FMT_plx, addr << 2);
- break;
- }
-
- trace_milkymist_sysctl_memory_read(addr << 2, r);
-
- return r;
-}
-
-static void sysctl_write(void *opaque, hwaddr addr, uint64_t value,
- unsigned size)
-{
- MilkymistSysctlState *s = opaque;
-
- trace_milkymist_sysctl_memory_write(addr, value);
-
- addr >>= 2;
- switch (addr) {
- case R_GPIO_OUT:
- case R_GPIO_INTEN:
- case R_TIMER0_COUNTER:
- case R_TIMER1_COUNTER:
- case R_DBG_SCRATCHPAD:
- s->regs[addr] = value;
- break;
- case R_TIMER0_COMPARE:
- ptimer_set_limit(s->ptimer0, value, 0);
- s->regs[addr] = value;
- break;
- case R_TIMER1_COMPARE:
- ptimer_set_limit(s->ptimer1, value, 0);
- s->regs[addr] = value;
- break;
- case R_TIMER0_CONTROL:
- s->regs[addr] = value;
- if (s->regs[R_TIMER0_CONTROL] & CTRL_ENABLE) {
- trace_milkymist_sysctl_start_timer0();
- ptimer_set_count(s->ptimer0,
- s->regs[R_TIMER0_COMPARE] - s->regs[R_TIMER0_COUNTER]);
- ptimer_run(s->ptimer0, 0);
- } else {
- trace_milkymist_sysctl_stop_timer0();
- ptimer_stop(s->ptimer0);
- }
- break;
- case R_TIMER1_CONTROL:
- s->regs[addr] = value;
- if (s->regs[R_TIMER1_CONTROL] & CTRL_ENABLE) {
- trace_milkymist_sysctl_start_timer1();
- ptimer_set_count(s->ptimer1,
- s->regs[R_TIMER1_COMPARE] - s->regs[R_TIMER1_COUNTER]);
- ptimer_run(s->ptimer1, 0);
- } else {
- trace_milkymist_sysctl_stop_timer1();
- ptimer_stop(s->ptimer1);
- }
- break;
- case R_ICAP:
- sysctl_icap_write(s, value);
- break;
- case R_DBG_WRITE_LOCK:
- s->regs[addr] = 1;
- break;
- case R_SYSTEM_ID:
- qemu_system_reset_request();
- break;
-
- case R_GPIO_IN:
- case R_CLK_FREQUENCY:
- case R_CAPABILITIES:
- error_report("milkymist_sysctl: write to read-only register 0x"
- TARGET_FMT_plx, addr << 2);
- break;
-
- default:
- error_report("milkymist_sysctl: write access to unknown register 0x"
- TARGET_FMT_plx, addr << 2);
- break;
- }
-}
-
-static const MemoryRegionOps sysctl_mmio_ops = {
- .read = sysctl_read,
- .write = sysctl_write,
- .valid = {
- .min_access_size = 4,
- .max_access_size = 4,
- },
- .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static void timer0_hit(void *opaque)
-{
- MilkymistSysctlState *s = opaque;
-
- if (!(s->regs[R_TIMER0_CONTROL] & CTRL_AUTORESTART)) {
- s->regs[R_TIMER0_CONTROL] &= ~CTRL_ENABLE;
- trace_milkymist_sysctl_stop_timer0();
- ptimer_stop(s->ptimer0);
- }
-
- trace_milkymist_sysctl_pulse_irq_timer0();
- qemu_irq_pulse(s->timer0_irq);
-}
-
-static void timer1_hit(void *opaque)
-{
- MilkymistSysctlState *s = opaque;
-
- if (!(s->regs[R_TIMER1_CONTROL] & CTRL_AUTORESTART)) {
- s->regs[R_TIMER1_CONTROL] &= ~CTRL_ENABLE;
- trace_milkymist_sysctl_stop_timer1();
- ptimer_stop(s->ptimer1);
- }
-
- trace_milkymist_sysctl_pulse_irq_timer1();
- qemu_irq_pulse(s->timer1_irq);
-}
-
-static void milkymist_sysctl_reset(DeviceState *d)
-{
- MilkymistSysctlState *s =
- container_of(d, MilkymistSysctlState, busdev.qdev);
- int i;
-
- for (i = 0; i < R_MAX; i++) {
- s->regs[i] = 0;
- }
-
- ptimer_stop(s->ptimer0);
- ptimer_stop(s->ptimer1);
-
- /* defaults */
- s->regs[R_ICAP] = ICAP_READY;
- s->regs[R_SYSTEM_ID] = s->systemid;
- s->regs[R_CLK_FREQUENCY] = s->freq_hz;
- s->regs[R_CAPABILITIES] = s->capabilities;
- s->regs[R_GPIO_IN] = s->strappings;
-}
-
-static int milkymist_sysctl_init(SysBusDevice *dev)
-{
- MilkymistSysctlState *s = FROM_SYSBUS(typeof(*s), dev);
-
- sysbus_init_irq(dev, &s->gpio_irq);
- sysbus_init_irq(dev, &s->timer0_irq);
- sysbus_init_irq(dev, &s->timer1_irq);
-
- s->bh0 = qemu_bh_new(timer0_hit, s);
- s->bh1 = qemu_bh_new(timer1_hit, s);
- s->ptimer0 = ptimer_init(s->bh0);
- s->ptimer1 = ptimer_init(s->bh1);
- ptimer_set_freq(s->ptimer0, s->freq_hz);
- ptimer_set_freq(s->ptimer1, s->freq_hz);
-
- memory_region_init_io(&s->regs_region, &sysctl_mmio_ops, s,
- "milkymist-sysctl", R_MAX * 4);
- sysbus_init_mmio(dev, &s->regs_region);
-
- return 0;
-}
-
-static const VMStateDescription vmstate_milkymist_sysctl = {
- .name = "milkymist-sysctl",
- .version_id = 1,
- .minimum_version_id = 1,
- .minimum_version_id_old = 1,
- .fields = (VMStateField[]) {
- VMSTATE_UINT32_ARRAY(regs, MilkymistSysctlState, R_MAX),
- VMSTATE_PTIMER(ptimer0, MilkymistSysctlState),
- VMSTATE_PTIMER(ptimer1, MilkymistSysctlState),
- VMSTATE_END_OF_LIST()
- }
-};
-
-static Property milkymist_sysctl_properties[] = {
- DEFINE_PROP_UINT32("frequency", MilkymistSysctlState,
- freq_hz, 80000000),
- DEFINE_PROP_UINT32("capabilities", MilkymistSysctlState,
- capabilities, 0x00000000),
- DEFINE_PROP_UINT32("systemid", MilkymistSysctlState,
- systemid, 0x10014d31),
- DEFINE_PROP_UINT32("gpio_strappings", MilkymistSysctlState,
- strappings, 0x00000001),
- DEFINE_PROP_END_OF_LIST(),
-};
-
-static void milkymist_sysctl_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-
- k->init = milkymist_sysctl_init;
- dc->reset = milkymist_sysctl_reset;
- dc->vmsd = &vmstate_milkymist_sysctl;
- dc->props = milkymist_sysctl_properties;
-}
-
-static const TypeInfo milkymist_sysctl_info = {
- .name = "milkymist-sysctl",
- .parent = TYPE_SYS_BUS_DEVICE,
- .instance_size = sizeof(MilkymistSysctlState),
- .class_init = milkymist_sysctl_class_init,
-};
-
-static void milkymist_sysctl_register_types(void)
-{
- type_register_static(&milkymist_sysctl_info);
-}
-
-type_init(milkymist_sysctl_register_types)
+++ /dev/null
-/*
- * 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/hw.h"
-#include "qemu/timer.h"
-#include "hw/arm/omap.h"
-
-/* GP timers */
-struct omap_gp_timer_s {
- MemoryRegion iomem;
- 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, hwaddr 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, hwaddr 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 void omap_gp_timer_write(void *opaque, hwaddr 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, hwaddr 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 const MemoryRegionOps omap_gp_timer_ops = {
- .old_mmio = {
- .read = {
- omap_badwidth_read32,
- omap_gp_timer_readh,
- omap_gp_timer_readw,
- },
- .write = {
- omap_badwidth_write32,
- omap_gp_timer_writeh,
- omap_gp_timer_write,
- },
- },
- .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-struct omap_gp_timer_s *omap_gp_timer_init(struct omap_target_agent_s *ta,
- qemu_irq irq, omap_clk fclk, omap_clk iclk)
-{
- struct omap_gp_timer_s *s = (struct omap_gp_timer_s *)
- g_malloc0(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);
-
- memory_region_init_io(&s->iomem, &omap_gp_timer_ops, s, "omap.gptimer",
- omap_l4_region_size(ta, 0));
- omap_l4_attach(ta, 0, &s->iomem);
-
- return s;
-}
+++ /dev/null
-/*
- * TI OMAP2 32kHz sync timer 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/hw.h"
-#include "qemu/timer.h"
-#include "hw/arm/omap.h"
-struct omap_synctimer_s {
- MemoryRegion iomem;
- uint32_t val;
- uint16_t readh;
-};
-
-/* 32-kHz Sync Timer of the OMAP2 */
-static uint32_t omap_synctimer_read(struct omap_synctimer_s *s) {
- return muldiv64(qemu_get_clock_ns(vm_clock), 0x8000, get_ticks_per_sec());
-}
-
-void omap_synctimer_reset(struct omap_synctimer_s *s)
-{
- s->val = omap_synctimer_read(s);
-}
-
-static uint32_t omap_synctimer_readw(void *opaque, hwaddr addr)
-{
- struct omap_synctimer_s *s = (struct omap_synctimer_s *) opaque;
-
- switch (addr) {
- case 0x00: /* 32KSYNCNT_REV */
- return 0x21;
-
- case 0x10: /* CR */
- return omap_synctimer_read(s) - s->val;
- }
-
- OMAP_BAD_REG(addr);
- return 0;
-}
-
-static uint32_t omap_synctimer_readh(void *opaque, hwaddr addr)
-{
- struct omap_synctimer_s *s = (struct omap_synctimer_s *) opaque;
- uint32_t ret;
-
- if (addr & 2)
- return s->readh;
- else {
- ret = omap_synctimer_readw(opaque, addr);
- s->readh = ret >> 16;
- return ret & 0xffff;
- }
-}
-
-static void omap_synctimer_write(void *opaque, hwaddr addr,
- uint32_t value)
-{
- OMAP_BAD_REG(addr);
-}
-
-static const MemoryRegionOps omap_synctimer_ops = {
- .old_mmio = {
- .read = {
- omap_badwidth_read32,
- omap_synctimer_readh,
- omap_synctimer_readw,
- },
- .write = {
- omap_badwidth_write32,
- omap_synctimer_write,
- omap_synctimer_write,
- },
- },
- .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-struct omap_synctimer_s *omap_synctimer_init(struct omap_target_agent_s *ta,
- struct omap_mpu_state_s *mpu, omap_clk fclk, omap_clk iclk)
-{
- struct omap_synctimer_s *s = g_malloc0(sizeof(*s));
-
- omap_synctimer_reset(s);
- memory_region_init_io(&s->iomem, &omap_synctimer_ops, s, "omap.synctimer",
- omap_l4_region_size(ta, 0));
- omap_l4_attach(ta, 0, &s->iomem);
-
- return s;
-}
+++ /dev/null
-/*
- * Intel PXA27X Keypad Controller emulation.
- *
- * Copyright (c) 2007 MontaVista Software, Inc
- * Written by Armin Kuster <akuster@kama-aina.net>
- * or <Akuster@mvista.com>
- *
- * This code is licensed under the GPLv2.
- *
- * Contributions after 2012-01-13 are licensed under the terms of the
- * GNU GPL, version 2 or (at your option) any later version.
- */
-
-#include "hw/hw.h"
-#include "hw/arm/pxa.h"
-#include "ui/console.h"
-
-/*
- * Keypad
- */
-#define KPC 0x00 /* Keypad Interface Control register */
-#define KPDK 0x08 /* Keypad Interface Direct Key register */
-#define KPREC 0x10 /* Keypad Interface Rotary Encoder register */
-#define KPMK 0x18 /* Keypad Interface Matrix Key register */
-#define KPAS 0x20 /* Keypad Interface Automatic Scan register */
-#define KPASMKP0 0x28 /* Keypad Interface Automatic Scan Multiple
- Key Presser register 0 */
-#define KPASMKP1 0x30 /* Keypad Interface Automatic Scan Multiple
- Key Presser register 1 */
-#define KPASMKP2 0x38 /* Keypad Interface Automatic Scan Multiple
- Key Presser register 2 */
-#define KPASMKP3 0x40 /* Keypad Interface Automatic Scan Multiple
- Key Presser register 3 */
-#define KPKDI 0x48 /* Keypad Interface Key Debounce Interval
- register */
-
-/* Keypad defines */
-#define KPC_AS (0x1 << 30) /* Automatic Scan bit */
-#define KPC_ASACT (0x1 << 29) /* Automatic Scan on Activity */
-#define KPC_MI (0x1 << 22) /* Matrix interrupt bit */
-#define KPC_IMKP (0x1 << 21) /* Ignore Multiple Key Press */
-#define KPC_MS7 (0x1 << 20) /* Matrix scan line 7 */
-#define KPC_MS6 (0x1 << 19) /* Matrix scan line 6 */
-#define KPC_MS5 (0x1 << 18) /* Matrix scan line 5 */
-#define KPC_MS4 (0x1 << 17) /* Matrix scan line 4 */
-#define KPC_MS3 (0x1 << 16) /* Matrix scan line 3 */
-#define KPC_MS2 (0x1 << 15) /* Matrix scan line 2 */
-#define KPC_MS1 (0x1 << 14) /* Matrix scan line 1 */
-#define KPC_MS0 (0x1 << 13) /* Matrix scan line 0 */
-#define KPC_ME (0x1 << 12) /* Matrix Keypad Enable */
-#define KPC_MIE (0x1 << 11) /* Matrix Interrupt Enable */
-#define KPC_DK_DEB_SEL (0x1 << 9) /* Direct Keypad Debounce Select */
-#define KPC_DI (0x1 << 5) /* Direct key interrupt bit */
-#define KPC_RE_ZERO_DEB (0x1 << 4) /* Rotary Encoder Zero Debounce */
-#define KPC_REE1 (0x1 << 3) /* Rotary Encoder1 Enable */
-#define KPC_REE0 (0x1 << 2) /* Rotary Encoder0 Enable */
-#define KPC_DE (0x1 << 1) /* Direct Keypad Enable */
-#define KPC_DIE (0x1 << 0) /* Direct Keypad interrupt Enable */
-
-#define KPDK_DKP (0x1 << 31)
-#define KPDK_DK7 (0x1 << 7)
-#define KPDK_DK6 (0x1 << 6)
-#define KPDK_DK5 (0x1 << 5)
-#define KPDK_DK4 (0x1 << 4)
-#define KPDK_DK3 (0x1 << 3)
-#define KPDK_DK2 (0x1 << 2)
-#define KPDK_DK1 (0x1 << 1)
-#define KPDK_DK0 (0x1 << 0)
-
-#define KPREC_OF1 (0x1 << 31)
-#define KPREC_UF1 (0x1 << 30)
-#define KPREC_OF0 (0x1 << 15)
-#define KPREC_UF0 (0x1 << 14)
-
-#define KPMK_MKP (0x1 << 31)
-#define KPAS_SO (0x1 << 31)
-#define KPASMKPx_SO (0x1 << 31)
-
-
-#define KPASMKPx_MKC(row, col) (1 << (row + 16 * (col % 2)))
-
-#define PXAKBD_MAXROW 8
-#define PXAKBD_MAXCOL 8
-
-struct PXA2xxKeyPadState {
- MemoryRegion iomem;
- qemu_irq irq;
- struct keymap *map;
- int pressed_cnt;
- int alt_code;
-
- uint32_t kpc;
- uint32_t kpdk;
- uint32_t kprec;
- uint32_t kpmk;
- uint32_t kpas;
- uint32_t kpasmkp[4];
- uint32_t kpkdi;
-};
-
-static void pxa27x_keypad_find_pressed_key(PXA2xxKeyPadState *kp, int *row, int *col)
-{
- int i;
- for (i = 0; i < 4; i++)
- {
- *col = i * 2;
- for (*row = 0; *row < 8; (*row)++) {
- if (kp->kpasmkp[i] & (1 << *row))
- return;
- }
- *col = i * 2 + 1;
- for (*row = 0; *row < 8; (*row)++) {
- if (kp->kpasmkp[i] & (1 << (*row + 16)))
- return;
- }
- }
-}
-
-static void pxa27x_keyboard_event (PXA2xxKeyPadState *kp, int keycode)
-{
- int row, col, rel, assert_irq = 0;
- uint32_t val;
-
- if (keycode == 0xe0) {
- kp->alt_code = 1;
- return;
- }
-
- if(!(kp->kpc & KPC_ME)) /* skip if not enabled */
- return;
-
- rel = (keycode & 0x80) ? 1 : 0; /* key release from qemu */
- keycode &= ~0x80; /* strip qemu key release bit */
- if (kp->alt_code) {
- keycode |= 0x80;
- kp->alt_code = 0;
- }
-
- row = kp->map[keycode].row;
- col = kp->map[keycode].column;
- if (row == -1 || col == -1) {
- return;
- }
-
- val = KPASMKPx_MKC(row, col);
- if (rel) {
- if (kp->kpasmkp[col / 2] & val) {
- kp->kpasmkp[col / 2] &= ~val;
- kp->pressed_cnt--;
- assert_irq = 1;
- }
- } else {
- if (!(kp->kpasmkp[col / 2] & val)) {
- kp->kpasmkp[col / 2] |= val;
- kp->pressed_cnt++;
- assert_irq = 1;
- }
- }
- kp->kpas = ((kp->pressed_cnt & 0x1f) << 26) | (0xf << 4) | 0xf;
- if (kp->pressed_cnt == 1) {
- kp->kpas &= ~((0xf << 4) | 0xf);
- if (rel) {
- pxa27x_keypad_find_pressed_key(kp, &row, &col);
- }
- kp->kpas |= ((row & 0xf) << 4) | (col & 0xf);
- }
-
- if (!(kp->kpc & (KPC_AS | KPC_ASACT)))
- assert_irq = 0;
-
- if (assert_irq && (kp->kpc & KPC_MIE)) {
- kp->kpc |= KPC_MI;
- qemu_irq_raise(kp->irq);
- }
-}
-
-static uint64_t pxa2xx_keypad_read(void *opaque, hwaddr offset,
- unsigned size)
-{
- PXA2xxKeyPadState *s = (PXA2xxKeyPadState *) opaque;
- uint32_t tmp;
-
- switch (offset) {
- case KPC:
- tmp = s->kpc;
- if(tmp & KPC_MI)
- s->kpc &= ~(KPC_MI);
- if(tmp & KPC_DI)
- s->kpc &= ~(KPC_DI);
- qemu_irq_lower(s->irq);
- return tmp;
- break;
- case KPDK:
- return s->kpdk;
- break;
- case KPREC:
- tmp = s->kprec;
- if(tmp & KPREC_OF1)
- s->kprec &= ~(KPREC_OF1);
- if(tmp & KPREC_UF1)
- s->kprec &= ~(KPREC_UF1);
- if(tmp & KPREC_OF0)
- s->kprec &= ~(KPREC_OF0);
- if(tmp & KPREC_UF0)
- s->kprec &= ~(KPREC_UF0);
- return tmp;
- break;
- case KPMK:
- tmp = s->kpmk;
- if(tmp & KPMK_MKP)
- s->kpmk &= ~(KPMK_MKP);
- return tmp;
- break;
- case KPAS:
- return s->kpas;
- break;
- case KPASMKP0:
- return s->kpasmkp[0];
- break;
- case KPASMKP1:
- return s->kpasmkp[1];
- break;
- case KPASMKP2:
- return s->kpasmkp[2];
- break;
- case KPASMKP3:
- return s->kpasmkp[3];
- break;
- case KPKDI:
- return s->kpkdi;
- break;
- default:
- hw_error("%s: Bad offset " REG_FMT "\n", __FUNCTION__, offset);
- }
-
- return 0;
-}
-
-static void pxa2xx_keypad_write(void *opaque, hwaddr offset,
- uint64_t value, unsigned size)
-{
- PXA2xxKeyPadState *s = (PXA2xxKeyPadState *) opaque;
-
- switch (offset) {
- case KPC:
- s->kpc = value;
- if (s->kpc & KPC_AS) {
- s->kpc &= ~(KPC_AS);
- }
- break;
- case KPDK:
- s->kpdk = value;
- break;
- case KPREC:
- s->kprec = value;
- break;
- case KPMK:
- s->kpmk = value;
- break;
- case KPAS:
- s->kpas = value;
- break;
- case KPASMKP0:
- s->kpasmkp[0] = value;
- break;
- case KPASMKP1:
- s->kpasmkp[1] = value;
- break;
- case KPASMKP2:
- s->kpasmkp[2] = value;
- break;
- case KPASMKP3:
- s->kpasmkp[3] = value;
- break;
- case KPKDI:
- s->kpkdi = value;
- break;
-
- default:
- hw_error("%s: Bad offset " REG_FMT "\n", __FUNCTION__, offset);
- }
-}
-
-static const MemoryRegionOps pxa2xx_keypad_ops = {
- .read = pxa2xx_keypad_read,
- .write = pxa2xx_keypad_write,
- .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static const VMStateDescription vmstate_pxa2xx_keypad = {
- .name = "pxa2xx_keypad",
- .version_id = 0,
- .minimum_version_id = 0,
- .minimum_version_id_old = 0,
- .fields = (VMStateField[]) {
- VMSTATE_UINT32(kpc, PXA2xxKeyPadState),
- VMSTATE_UINT32(kpdk, PXA2xxKeyPadState),
- VMSTATE_UINT32(kprec, PXA2xxKeyPadState),
- VMSTATE_UINT32(kpmk, PXA2xxKeyPadState),
- VMSTATE_UINT32(kpas, PXA2xxKeyPadState),
- VMSTATE_UINT32_ARRAY(kpasmkp, PXA2xxKeyPadState, 4),
- VMSTATE_UINT32(kpkdi, PXA2xxKeyPadState),
- VMSTATE_END_OF_LIST()
- }
-};
-
-PXA2xxKeyPadState *pxa27x_keypad_init(MemoryRegion *sysmem,
- hwaddr base,
- qemu_irq irq)
-{
- PXA2xxKeyPadState *s;
-
- s = (PXA2xxKeyPadState *) g_malloc0(sizeof(PXA2xxKeyPadState));
- s->irq = irq;
-
- memory_region_init_io(&s->iomem, &pxa2xx_keypad_ops, s,
- "pxa2xx-keypad", 0x00100000);
- memory_region_add_subregion(sysmem, base, &s->iomem);
-
- vmstate_register(NULL, 0, &vmstate_pxa2xx_keypad, s);
-
- return s;
-}
-
-void pxa27x_register_keypad(PXA2xxKeyPadState *kp, struct keymap *map,
- int size)
-{
- if(!map || size < 0x80) {
- fprintf(stderr, "%s - No PXA keypad map defined\n", __FUNCTION__);
- exit(-1);
- }
-
- kp->map = map;
- qemu_add_kbd_event_handler((QEMUPutKBDEvent *) pxa27x_keyboard_event, kp);
-}
+++ /dev/null
-/*
- * Intel XScale PXA255/270 OS Timers.
- *
- * Copyright (c) 2006 Openedhand Ltd.
- * Copyright (c) 2006 Thorsten Zitterell
- *
- * This code is licensed under the GPL.
- */
-
-#include "hw/hw.h"
-#include "qemu/timer.h"
-#include "sysemu/sysemu.h"
-#include "hw/arm/pxa.h"
-#include "hw/sysbus.h"
-
-#define OSMR0 0x00
-#define OSMR1 0x04
-#define OSMR2 0x08
-#define OSMR3 0x0c
-#define OSMR4 0x80
-#define OSMR5 0x84
-#define OSMR6 0x88
-#define OSMR7 0x8c
-#define OSMR8 0x90
-#define OSMR9 0x94
-#define OSMR10 0x98
-#define OSMR11 0x9c
-#define OSCR 0x10 /* OS Timer Count */
-#define OSCR4 0x40
-#define OSCR5 0x44
-#define OSCR6 0x48
-#define OSCR7 0x4c
-#define OSCR8 0x50
-#define OSCR9 0x54
-#define OSCR10 0x58
-#define OSCR11 0x5c
-#define OSSR 0x14 /* Timer status register */
-#define OWER 0x18
-#define OIER 0x1c /* Interrupt enable register 3-0 to E3-E0 */
-#define OMCR4 0xc0 /* OS Match Control registers */
-#define OMCR5 0xc4
-#define OMCR6 0xc8
-#define OMCR7 0xcc
-#define OMCR8 0xd0
-#define OMCR9 0xd4
-#define OMCR10 0xd8
-#define OMCR11 0xdc
-#define OSNR 0x20
-
-#define PXA25X_FREQ 3686400 /* 3.6864 MHz */
-#define PXA27X_FREQ 3250000 /* 3.25 MHz */
-
-static int pxa2xx_timer4_freq[8] = {
- [0] = 0,
- [1] = 32768,
- [2] = 1000,
- [3] = 1,
- [4] = 1000000,
- /* [5] is the "Externally supplied clock". Assign if necessary. */
- [5 ... 7] = 0,
-};
-
-typedef struct PXA2xxTimerInfo PXA2xxTimerInfo;
-
-typedef struct {
- uint32_t value;
- qemu_irq irq;
- QEMUTimer *qtimer;
- int num;
- PXA2xxTimerInfo *info;
-} PXA2xxTimer0;
-
-typedef struct {
- PXA2xxTimer0 tm;
- int32_t oldclock;
- int32_t clock;
- uint64_t lastload;
- uint32_t freq;
- uint32_t control;
-} PXA2xxTimer4;
-
-struct PXA2xxTimerInfo {
- SysBusDevice busdev;
- MemoryRegion iomem;
- uint32_t flags;
-
- int32_t clock;
- int32_t oldclock;
- uint64_t lastload;
- uint32_t freq;
- PXA2xxTimer0 timer[4];
- uint32_t events;
- uint32_t irq_enabled;
- uint32_t reset3;
- uint32_t snapshot;
-
- qemu_irq irq4;
- PXA2xxTimer4 tm4[8];
-};
-
-#define PXA2XX_TIMER_HAVE_TM4 0
-
-static inline int pxa2xx_timer_has_tm4(PXA2xxTimerInfo *s)
-{
- return s->flags & (1 << PXA2XX_TIMER_HAVE_TM4);
-}
-
-static void pxa2xx_timer_update(void *opaque, uint64_t now_qemu)
-{
- PXA2xxTimerInfo *s = (PXA2xxTimerInfo *) opaque;
- int i;
- uint32_t now_vm;
- uint64_t new_qemu;
-
- now_vm = s->clock +
- muldiv64(now_qemu - s->lastload, s->freq, get_ticks_per_sec());
-
- for (i = 0; i < 4; i ++) {
- new_qemu = now_qemu + muldiv64((uint32_t) (s->timer[i].value - now_vm),
- get_ticks_per_sec(), s->freq);
- qemu_mod_timer(s->timer[i].qtimer, new_qemu);
- }
-}
-
-static void pxa2xx_timer_update4(void *opaque, uint64_t now_qemu, int n)
-{
- PXA2xxTimerInfo *s = (PXA2xxTimerInfo *) opaque;
- uint32_t now_vm;
- uint64_t new_qemu;
- static const int counters[8] = { 0, 0, 0, 0, 4, 4, 6, 6 };
- int counter;
-
- if (s->tm4[n].control & (1 << 7))
- counter = n;
- else
- counter = counters[n];
-
- if (!s->tm4[counter].freq) {
- qemu_del_timer(s->tm4[n].tm.qtimer);
- return;
- }
-
- now_vm = s->tm4[counter].clock + muldiv64(now_qemu -
- s->tm4[counter].lastload,
- s->tm4[counter].freq, get_ticks_per_sec());
-
- new_qemu = now_qemu + muldiv64((uint32_t) (s->tm4[n].tm.value - now_vm),
- get_ticks_per_sec(), s->tm4[counter].freq);
- qemu_mod_timer(s->tm4[n].tm.qtimer, new_qemu);
-}
-
-static uint64_t pxa2xx_timer_read(void *opaque, hwaddr offset,
- unsigned size)
-{
- PXA2xxTimerInfo *s = (PXA2xxTimerInfo *) opaque;
- int tm = 0;
-
- switch (offset) {
- case OSMR3: tm ++;
- /* fall through */
- case OSMR2: tm ++;
- /* fall through */
- case OSMR1: tm ++;
- /* fall through */
- case OSMR0:
- return s->timer[tm].value;
- case OSMR11: tm ++;
- /* fall through */
- case OSMR10: tm ++;
- /* fall through */
- case OSMR9: tm ++;
- /* fall through */
- case OSMR8: tm ++;
- /* fall through */
- case OSMR7: tm ++;
- /* fall through */
- case OSMR6: tm ++;
- /* fall through */
- case OSMR5: tm ++;
- /* fall through */
- case OSMR4:
- if (!pxa2xx_timer_has_tm4(s))
- goto badreg;
- return s->tm4[tm].tm.value;
- case OSCR:
- return s->clock + muldiv64(qemu_get_clock_ns(vm_clock) -
- s->lastload, s->freq, get_ticks_per_sec());
- case OSCR11: tm ++;
- /* fall through */
- case OSCR10: tm ++;
- /* fall through */
- case OSCR9: tm ++;
- /* fall through */
- case OSCR8: tm ++;
- /* fall through */
- case OSCR7: tm ++;
- /* fall through */
- case OSCR6: tm ++;
- /* fall through */
- case OSCR5: tm ++;
- /* fall through */
- case OSCR4:
- if (!pxa2xx_timer_has_tm4(s))
- goto badreg;
-
- if ((tm == 9 - 4 || tm == 11 - 4) && (s->tm4[tm].control & (1 << 9))) {
- if (s->tm4[tm - 1].freq)
- s->snapshot = s->tm4[tm - 1].clock + muldiv64(
- qemu_get_clock_ns(vm_clock) -
- s->tm4[tm - 1].lastload,
- s->tm4[tm - 1].freq, get_ticks_per_sec());
- else
- s->snapshot = s->tm4[tm - 1].clock;
- }
-
- if (!s->tm4[tm].freq)
- return s->tm4[tm].clock;
- return s->tm4[tm].clock + muldiv64(qemu_get_clock_ns(vm_clock) -
- s->tm4[tm].lastload, s->tm4[tm].freq, get_ticks_per_sec());
- case OIER:
- return s->irq_enabled;
- case OSSR: /* Status register */
- return s->events;
- case OWER:
- return s->reset3;
- case OMCR11: tm ++;
- /* fall through */
- case OMCR10: tm ++;
- /* fall through */
- case OMCR9: tm ++;
- /* fall through */
- case OMCR8: tm ++;
- /* fall through */
- case OMCR7: tm ++;
- /* fall through */
- case OMCR6: tm ++;
- /* fall through */
- case OMCR5: tm ++;
- /* fall through */
- case OMCR4:
- if (!pxa2xx_timer_has_tm4(s))
- goto badreg;
- return s->tm4[tm].control;
- case OSNR:
- return s->snapshot;
- default:
- badreg:
- hw_error("pxa2xx_timer_read: Bad offset " REG_FMT "\n", offset);
- }
-
- return 0;
-}
-
-static void pxa2xx_timer_write(void *opaque, hwaddr offset,
- uint64_t value, unsigned size)
-{
- int i, tm = 0;
- PXA2xxTimerInfo *s = (PXA2xxTimerInfo *) opaque;
-
- switch (offset) {
- case OSMR3: tm ++;
- /* fall through */
- case OSMR2: tm ++;
- /* fall through */
- case OSMR1: tm ++;
- /* fall through */
- case OSMR0:
- s->timer[tm].value = value;
- pxa2xx_timer_update(s, qemu_get_clock_ns(vm_clock));
- break;
- case OSMR11: tm ++;
- /* fall through */
- case OSMR10: tm ++;
- /* fall through */
- case OSMR9: tm ++;
- /* fall through */
- case OSMR8: tm ++;
- /* fall through */
- case OSMR7: tm ++;
- /* fall through */
- case OSMR6: tm ++;
- /* fall through */
- case OSMR5: tm ++;
- /* fall through */
- case OSMR4:
- if (!pxa2xx_timer_has_tm4(s))
- goto badreg;
- s->tm4[tm].tm.value = value;
- pxa2xx_timer_update4(s, qemu_get_clock_ns(vm_clock), tm);
- break;
- case OSCR:
- s->oldclock = s->clock;
- s->lastload = qemu_get_clock_ns(vm_clock);
- s->clock = value;
- pxa2xx_timer_update(s, s->lastload);
- break;
- case OSCR11: tm ++;
- /* fall through */
- case OSCR10: tm ++;
- /* fall through */
- case OSCR9: tm ++;
- /* fall through */
- case OSCR8: tm ++;
- /* fall through */
- case OSCR7: tm ++;
- /* fall through */
- case OSCR6: tm ++;
- /* fall through */
- case OSCR5: tm ++;
- /* fall through */
- case OSCR4:
- if (!pxa2xx_timer_has_tm4(s))
- goto badreg;
- s->tm4[tm].oldclock = s->tm4[tm].clock;
- s->tm4[tm].lastload = qemu_get_clock_ns(vm_clock);
- s->tm4[tm].clock = value;
- pxa2xx_timer_update4(s, s->tm4[tm].lastload, tm);
- break;
- case OIER:
- s->irq_enabled = value & 0xfff;
- break;
- case OSSR: /* Status register */
- value &= s->events;
- s->events &= ~value;
- for (i = 0; i < 4; i ++, value >>= 1)
- if (value & 1)
- qemu_irq_lower(s->timer[i].irq);
- if (pxa2xx_timer_has_tm4(s) && !(s->events & 0xff0) && value)
- qemu_irq_lower(s->irq4);
- break;
- case OWER: /* XXX: Reset on OSMR3 match? */
- s->reset3 = value;
- break;
- case OMCR7: tm ++;
- /* fall through */
- case OMCR6: tm ++;
- /* fall through */
- case OMCR5: tm ++;
- /* fall through */
- case OMCR4:
- if (!pxa2xx_timer_has_tm4(s))
- goto badreg;
- s->tm4[tm].control = value & 0x0ff;
- /* XXX Stop if running (shouldn't happen) */
- if ((value & (1 << 7)) || tm == 0)
- s->tm4[tm].freq = pxa2xx_timer4_freq[value & 7];
- else {
- s->tm4[tm].freq = 0;
- pxa2xx_timer_update4(s, qemu_get_clock_ns(vm_clock), tm);
- }
- break;
- case OMCR11: tm ++;
- /* fall through */
- case OMCR10: tm ++;
- /* fall through */
- case OMCR9: tm ++;
- /* fall through */
- case OMCR8: tm += 4;
- if (!pxa2xx_timer_has_tm4(s))
- goto badreg;
- s->tm4[tm].control = value & 0x3ff;
- /* XXX Stop if running (shouldn't happen) */
- if ((value & (1 << 7)) || !(tm & 1))
- s->tm4[tm].freq =
- pxa2xx_timer4_freq[(value & (1 << 8)) ? 0 : (value & 7)];
- else {
- s->tm4[tm].freq = 0;
- pxa2xx_timer_update4(s, qemu_get_clock_ns(vm_clock), tm);
- }
- break;
- default:
- badreg:
- hw_error("pxa2xx_timer_write: Bad offset " REG_FMT "\n", offset);
- }
-}
-
-static const MemoryRegionOps pxa2xx_timer_ops = {
- .read = pxa2xx_timer_read,
- .write = pxa2xx_timer_write,
- .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static void pxa2xx_timer_tick(void *opaque)
-{
- PXA2xxTimer0 *t = (PXA2xxTimer0 *) opaque;
- PXA2xxTimerInfo *i = t->info;
-
- if (i->irq_enabled & (1 << t->num)) {
- i->events |= 1 << t->num;
- qemu_irq_raise(t->irq);
- }
-
- if (t->num == 3)
- if (i->reset3 & 1) {
- i->reset3 = 0;
- qemu_system_reset_request();
- }
-}
-
-static void pxa2xx_timer_tick4(void *opaque)
-{
- PXA2xxTimer4 *t = (PXA2xxTimer4 *) opaque;
- PXA2xxTimerInfo *i = (PXA2xxTimerInfo *) t->tm.info;
-
- pxa2xx_timer_tick(&t->tm);
- if (t->control & (1 << 3))
- t->clock = 0;
- if (t->control & (1 << 6))
- pxa2xx_timer_update4(i, qemu_get_clock_ns(vm_clock), t->tm.num - 4);
- if (i->events & 0xff0)
- qemu_irq_raise(i->irq4);
-}
-
-static int pxa25x_timer_post_load(void *opaque, int version_id)
-{
- PXA2xxTimerInfo *s = (PXA2xxTimerInfo *) opaque;
- int64_t now;
- int i;
-
- now = qemu_get_clock_ns(vm_clock);
- pxa2xx_timer_update(s, now);
-
- if (pxa2xx_timer_has_tm4(s))
- for (i = 0; i < 8; i ++)
- pxa2xx_timer_update4(s, now, i);
-
- return 0;
-}
-
-static int pxa2xx_timer_init(SysBusDevice *dev)
-{
- int i;
- PXA2xxTimerInfo *s;
-
- s = FROM_SYSBUS(PXA2xxTimerInfo, dev);
- s->irq_enabled = 0;
- s->oldclock = 0;
- s->clock = 0;
- s->lastload = qemu_get_clock_ns(vm_clock);
- s->reset3 = 0;
-
- for (i = 0; i < 4; i ++) {
- s->timer[i].value = 0;
- sysbus_init_irq(dev, &s->timer[i].irq);
- s->timer[i].info = s;
- s->timer[i].num = i;
- s->timer[i].qtimer = qemu_new_timer_ns(vm_clock,
- pxa2xx_timer_tick, &s->timer[i]);
- }
- if (s->flags & (1 << PXA2XX_TIMER_HAVE_TM4)) {
- sysbus_init_irq(dev, &s->irq4);
-
- for (i = 0; i < 8; i ++) {
- s->tm4[i].tm.value = 0;
- s->tm4[i].tm.info = s;
- s->tm4[i].tm.num = i + 4;
- s->tm4[i].freq = 0;
- s->tm4[i].control = 0x0;
- s->tm4[i].tm.qtimer = qemu_new_timer_ns(vm_clock,
- pxa2xx_timer_tick4, &s->tm4[i]);
- }
- }
-
- memory_region_init_io(&s->iomem, &pxa2xx_timer_ops, s,
- "pxa2xx-timer", 0x00001000);
- sysbus_init_mmio(dev, &s->iomem);
-
- return 0;
-}
-
-static const VMStateDescription vmstate_pxa2xx_timer0_regs = {
- .name = "pxa2xx_timer0",
- .version_id = 2,
- .minimum_version_id = 2,
- .minimum_version_id_old = 2,
- .fields = (VMStateField[]) {
- VMSTATE_UINT32(value, PXA2xxTimer0),
- VMSTATE_END_OF_LIST(),
- },
-};
-
-static const VMStateDescription vmstate_pxa2xx_timer4_regs = {
- .name = "pxa2xx_timer4",
- .version_id = 1,
- .minimum_version_id = 1,
- .minimum_version_id_old = 1,
- .fields = (VMStateField[]) {
- VMSTATE_STRUCT(tm, PXA2xxTimer4, 1,
- vmstate_pxa2xx_timer0_regs, PXA2xxTimer0),
- VMSTATE_INT32(oldclock, PXA2xxTimer4),
- VMSTATE_INT32(clock, PXA2xxTimer4),
- VMSTATE_UINT64(lastload, PXA2xxTimer4),
- VMSTATE_UINT32(freq, PXA2xxTimer4),
- VMSTATE_UINT32(control, PXA2xxTimer4),
- VMSTATE_END_OF_LIST(),
- },
-};
-
-static bool pxa2xx_timer_has_tm4_test(void *opaque, int version_id)
-{
- return pxa2xx_timer_has_tm4(opaque);
-}
-
-static const VMStateDescription vmstate_pxa2xx_timer_regs = {
- .name = "pxa2xx_timer",
- .version_id = 1,
- .minimum_version_id = 1,
- .minimum_version_id_old = 1,
- .post_load = pxa25x_timer_post_load,
- .fields = (VMStateField[]) {
- VMSTATE_INT32(clock, PXA2xxTimerInfo),
- VMSTATE_INT32(oldclock, PXA2xxTimerInfo),
- VMSTATE_UINT64(lastload, PXA2xxTimerInfo),
- VMSTATE_STRUCT_ARRAY(timer, PXA2xxTimerInfo, 4, 1,
- vmstate_pxa2xx_timer0_regs, PXA2xxTimer0),
- VMSTATE_UINT32(events, PXA2xxTimerInfo),
- VMSTATE_UINT32(irq_enabled, PXA2xxTimerInfo),
- VMSTATE_UINT32(reset3, PXA2xxTimerInfo),
- VMSTATE_UINT32(snapshot, PXA2xxTimerInfo),
- VMSTATE_STRUCT_ARRAY_TEST(tm4, PXA2xxTimerInfo, 8,
- pxa2xx_timer_has_tm4_test, 0,
- vmstate_pxa2xx_timer4_regs, PXA2xxTimer4),
- VMSTATE_END_OF_LIST(),
- }
-};
-
-static Property pxa25x_timer_dev_properties[] = {
- DEFINE_PROP_UINT32("freq", PXA2xxTimerInfo, freq, PXA25X_FREQ),
- DEFINE_PROP_BIT("tm4", PXA2xxTimerInfo, flags,
- PXA2XX_TIMER_HAVE_TM4, false),
- DEFINE_PROP_END_OF_LIST(),
-};
-
-static void pxa25x_timer_dev_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-
- k->init = pxa2xx_timer_init;
- dc->desc = "PXA25x timer";
- dc->vmsd = &vmstate_pxa2xx_timer_regs;
- dc->props = pxa25x_timer_dev_properties;
-}
-
-static const TypeInfo pxa25x_timer_dev_info = {
- .name = "pxa25x-timer",
- .parent = TYPE_SYS_BUS_DEVICE,
- .instance_size = sizeof(PXA2xxTimerInfo),
- .class_init = pxa25x_timer_dev_class_init,
-};
-
-static Property pxa27x_timer_dev_properties[] = {
- DEFINE_PROP_UINT32("freq", PXA2xxTimerInfo, freq, PXA27X_FREQ),
- DEFINE_PROP_BIT("tm4", PXA2xxTimerInfo, flags,
- PXA2XX_TIMER_HAVE_TM4, true),
- DEFINE_PROP_END_OF_LIST(),
-};
-
-static void pxa27x_timer_dev_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-
- k->init = pxa2xx_timer_init;
- dc->desc = "PXA27x timer";
- dc->vmsd = &vmstate_pxa2xx_timer_regs;
- dc->props = pxa27x_timer_dev_properties;
-}
-
-static const TypeInfo pxa27x_timer_dev_info = {
- .name = "pxa27x-timer",
- .parent = TYPE_SYS_BUS_DEVICE,
- .instance_size = sizeof(PXA2xxTimerInfo),
- .class_init = pxa27x_timer_dev_class_init,
-};
-
-static void pxa2xx_timer_register_types(void)
-{
- type_register_static(&pxa25x_timer_dev_info);
- type_register_static(&pxa27x_timer_dev_info);
-}
-
-type_init(pxa2xx_timer_register_types)
-obj-y += sh_timer.o sh_intc.o sh_pci.o
+obj-y += sh_intc.o sh_pci.o
obj-y := $(addprefix ../,$(obj-y))
+++ /dev/null
-/*
- * SuperH Timer modules.
- *
- * Copyright (c) 2007 Magnus Damm
- * Based on arm_timer.c by Paul Brook
- * Copyright (c) 2005-2006 CodeSourcery.
- *
- * This code is licensed under the GPL.
- */
-
-#include "hw/hw.h"
-#include "hw/sh4/sh.h"
-#include "qemu/timer.h"
-#include "exec/address-spaces.h"
-#include "hw/ptimer.h"
-
-//#define DEBUG_TIMER
-
-#define TIMER_TCR_TPSC (7 << 0)
-#define TIMER_TCR_CKEG (3 << 3)
-#define TIMER_TCR_UNIE (1 << 5)
-#define TIMER_TCR_ICPE (3 << 6)
-#define TIMER_TCR_UNF (1 << 8)
-#define TIMER_TCR_ICPF (1 << 9)
-#define TIMER_TCR_RESERVED (0x3f << 10)
-
-#define TIMER_FEAT_CAPT (1 << 0)
-#define TIMER_FEAT_EXTCLK (1 << 1)
-
-#define OFFSET_TCOR 0
-#define OFFSET_TCNT 1
-#define OFFSET_TCR 2
-#define OFFSET_TCPR 3
-
-typedef struct {
- ptimer_state *timer;
- uint32_t tcnt;
- uint32_t tcor;
- uint32_t tcr;
- uint32_t tcpr;
- int freq;
- int int_level;
- int old_level;
- int feat;
- int enabled;
- qemu_irq irq;
-} sh_timer_state;
-
-/* Check all active timers, and schedule the next timer interrupt. */
-
-static void sh_timer_update(sh_timer_state *s)
-{
- int new_level = s->int_level && (s->tcr & TIMER_TCR_UNIE);
-
- if (new_level != s->old_level)
- qemu_set_irq (s->irq, new_level);
-
- s->old_level = s->int_level;
- s->int_level = new_level;
-}
-
-static uint32_t sh_timer_read(void *opaque, hwaddr offset)
-{
- sh_timer_state *s = (sh_timer_state *)opaque;
-
- switch (offset >> 2) {
- case OFFSET_TCOR:
- return s->tcor;
- case OFFSET_TCNT:
- return ptimer_get_count(s->timer);
- case OFFSET_TCR:
- return s->tcr | (s->int_level ? TIMER_TCR_UNF : 0);
- case OFFSET_TCPR:
- if (s->feat & TIMER_FEAT_CAPT)
- return s->tcpr;
- default:
- hw_error("sh_timer_read: Bad offset %x\n", (int)offset);
- return 0;
- }
-}
-
-static void sh_timer_write(void *opaque, hwaddr offset,
- uint32_t value)
-{
- sh_timer_state *s = (sh_timer_state *)opaque;
- int freq;
-
- switch (offset >> 2) {
- case OFFSET_TCOR:
- s->tcor = value;
- ptimer_set_limit(s->timer, s->tcor, 0);
- break;
- case OFFSET_TCNT:
- s->tcnt = value;
- ptimer_set_count(s->timer, s->tcnt);
- break;
- case OFFSET_TCR:
- if (s->enabled) {
- /* Pause the timer if it is running. This may cause some
- inaccuracy dure to rounding, but avoids a whole lot of other
- messyness. */
- ptimer_stop(s->timer);
- }
- freq = s->freq;
- /* ??? Need to recalculate expiry time after changing divisor. */
- switch (value & TIMER_TCR_TPSC) {
- case 0: freq >>= 2; break;
- case 1: freq >>= 4; break;
- case 2: freq >>= 6; break;
- case 3: freq >>= 8; break;
- case 4: freq >>= 10; break;
- case 6:
- case 7: if (s->feat & TIMER_FEAT_EXTCLK) break;
- default: hw_error("sh_timer_write: Reserved TPSC value\n"); break;
- }
- switch ((value & TIMER_TCR_CKEG) >> 3) {
- case 0: break;
- case 1:
- case 2:
- case 3: if (s->feat & TIMER_FEAT_EXTCLK) break;
- default: hw_error("sh_timer_write: Reserved CKEG value\n"); break;
- }
- switch ((value & TIMER_TCR_ICPE) >> 6) {
- case 0: break;
- case 2:
- case 3: if (s->feat & TIMER_FEAT_CAPT) break;
- default: hw_error("sh_timer_write: Reserved ICPE value\n"); break;
- }
- if ((value & TIMER_TCR_UNF) == 0)
- s->int_level = 0;
-
- value &= ~TIMER_TCR_UNF;
-
- if ((value & TIMER_TCR_ICPF) && (!(s->feat & TIMER_FEAT_CAPT)))
- hw_error("sh_timer_write: Reserved ICPF value\n");
-
- value &= ~TIMER_TCR_ICPF; /* capture not supported */
-
- if (value & TIMER_TCR_RESERVED)
- hw_error("sh_timer_write: Reserved TCR bits set\n");
- s->tcr = value;
- ptimer_set_limit(s->timer, s->tcor, 0);
- ptimer_set_freq(s->timer, freq);
- if (s->enabled) {
- /* Restart the timer if still enabled. */
- ptimer_run(s->timer, 0);
- }
- break;
- case OFFSET_TCPR:
- if (s->feat & TIMER_FEAT_CAPT) {
- s->tcpr = value;
- break;
- }
- default:
- hw_error("sh_timer_write: Bad offset %x\n", (int)offset);
- }
- sh_timer_update(s);
-}
-
-static void sh_timer_start_stop(void *opaque, int enable)
-{
- sh_timer_state *s = (sh_timer_state *)opaque;
-
-#ifdef DEBUG_TIMER
- printf("sh_timer_start_stop %d (%d)\n", enable, s->enabled);
-#endif
-
- if (s->enabled && !enable) {
- ptimer_stop(s->timer);
- }
- if (!s->enabled && enable) {
- ptimer_run(s->timer, 0);
- }
- s->enabled = !!enable;
-
-#ifdef DEBUG_TIMER
- printf("sh_timer_start_stop done %d\n", s->enabled);
-#endif
-}
-
-static void sh_timer_tick(void *opaque)
-{
- sh_timer_state *s = (sh_timer_state *)opaque;
- s->int_level = s->enabled;
- sh_timer_update(s);
-}
-
-static void *sh_timer_init(uint32_t freq, int feat, qemu_irq irq)
-{
- sh_timer_state *s;
- QEMUBH *bh;
-
- s = (sh_timer_state *)g_malloc0(sizeof(sh_timer_state));
- s->freq = freq;
- s->feat = feat;
- s->tcor = 0xffffffff;
- s->tcnt = 0xffffffff;
- s->tcpr = 0xdeadbeef;
- s->tcr = 0;
- s->enabled = 0;
- s->irq = irq;
-
- bh = qemu_bh_new(sh_timer_tick, s);
- s->timer = ptimer_init(bh);
-
- sh_timer_write(s, OFFSET_TCOR >> 2, s->tcor);
- sh_timer_write(s, OFFSET_TCNT >> 2, s->tcnt);
- sh_timer_write(s, OFFSET_TCPR >> 2, s->tcpr);
- sh_timer_write(s, OFFSET_TCR >> 2, s->tcpr);
- /* ??? Save/restore. */
- return s;
-}
-
-typedef struct {
- MemoryRegion iomem;
- MemoryRegion iomem_p4;
- MemoryRegion iomem_a7;
- void *timer[3];
- int level[3];
- uint32_t tocr;
- uint32_t tstr;
- int feat;
-} tmu012_state;
-
-static uint64_t tmu012_read(void *opaque, hwaddr offset,
- unsigned size)
-{
- tmu012_state *s = (tmu012_state *)opaque;
-
-#ifdef DEBUG_TIMER
- printf("tmu012_read 0x%lx\n", (unsigned long) offset);
-#endif
-
- if (offset >= 0x20) {
- if (!(s->feat & TMU012_FEAT_3CHAN))
- hw_error("tmu012_write: Bad channel offset %x\n", (int)offset);
- return sh_timer_read(s->timer[2], offset - 0x20);
- }
-
- if (offset >= 0x14)
- return sh_timer_read(s->timer[1], offset - 0x14);
-
- if (offset >= 0x08)
- return sh_timer_read(s->timer[0], offset - 0x08);
-
- if (offset == 4)
- return s->tstr;
-
- if ((s->feat & TMU012_FEAT_TOCR) && offset == 0)
- return s->tocr;
-
- hw_error("tmu012_write: Bad offset %x\n", (int)offset);
- return 0;
-}
-
-static void tmu012_write(void *opaque, hwaddr offset,
- uint64_t value, unsigned size)
-{
- tmu012_state *s = (tmu012_state *)opaque;
-
-#ifdef DEBUG_TIMER
- printf("tmu012_write 0x%lx 0x%08x\n", (unsigned long) offset, value);
-#endif
-
- if (offset >= 0x20) {
- if (!(s->feat & TMU012_FEAT_3CHAN))
- hw_error("tmu012_write: Bad channel offset %x\n", (int)offset);
- sh_timer_write(s->timer[2], offset - 0x20, value);
- return;
- }
-
- if (offset >= 0x14) {
- sh_timer_write(s->timer[1], offset - 0x14, value);
- return;
- }
-
- if (offset >= 0x08) {
- sh_timer_write(s->timer[0], offset - 0x08, value);
- return;
- }
-
- if (offset == 4) {
- sh_timer_start_stop(s->timer[0], value & (1 << 0));
- sh_timer_start_stop(s->timer[1], value & (1 << 1));
- if (s->feat & TMU012_FEAT_3CHAN)
- sh_timer_start_stop(s->timer[2], value & (1 << 2));
- else
- if (value & (1 << 2))
- hw_error("tmu012_write: Bad channel\n");
-
- s->tstr = value;
- return;
- }
-
- if ((s->feat & TMU012_FEAT_TOCR) && offset == 0) {
- s->tocr = value & (1 << 0);
- }
-}
-
-static const MemoryRegionOps tmu012_ops = {
- .read = tmu012_read,
- .write = tmu012_write,
- .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-void tmu012_init(MemoryRegion *sysmem, hwaddr base,
- int feat, uint32_t freq,
- qemu_irq ch0_irq, qemu_irq ch1_irq,
- qemu_irq ch2_irq0, qemu_irq ch2_irq1)
-{
- tmu012_state *s;
- int timer_feat = (feat & TMU012_FEAT_EXTCLK) ? TIMER_FEAT_EXTCLK : 0;
-
- s = (tmu012_state *)g_malloc0(sizeof(tmu012_state));
- s->feat = feat;
- s->timer[0] = sh_timer_init(freq, timer_feat, ch0_irq);
- s->timer[1] = sh_timer_init(freq, timer_feat, ch1_irq);
- if (feat & TMU012_FEAT_3CHAN)
- s->timer[2] = sh_timer_init(freq, timer_feat | TIMER_FEAT_CAPT,
- ch2_irq0); /* ch2_irq1 not supported */
-
- memory_region_init_io(&s->iomem, &tmu012_ops, s,
- "timer", 0x100000000ULL);
-
- memory_region_init_alias(&s->iomem_p4, "timer-p4",
- &s->iomem, 0, 0x1000);
- memory_region_add_subregion(sysmem, P4ADDR(base), &s->iomem_p4);
-
- memory_region_init_alias(&s->iomem_a7, "timer-a7",
- &s->iomem, 0, 0x1000);
- memory_region_add_subregion(sysmem, A7ADDR(base), &s->iomem_a7);
- /* ??? Save/restore. */
-}
+++ /dev/null
-/*
- * QEMU Sparc SLAVIO timer controller emulation
- *
- * Copyright (c) 2003-2005 Fabrice Bellard
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-#include "hw/sparc/sun4m.h"
-#include "qemu/timer.h"
-#include "hw/ptimer.h"
-#include "hw/sysbus.h"
-#include "trace.h"
-
-/*
- * Registers of hardware timer in sun4m.
- *
- * This is the timer/counter part of chip STP2001 (Slave I/O), also
- * produced as NCR89C105. See
- * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C105.txt
- *
- * The 31-bit counter is incremented every 500ns by bit 9. Bits 8..0
- * are zero. Bit 31 is 1 when count has been reached.
- *
- * Per-CPU timers interrupt local CPU, system timer uses normal
- * interrupt routing.
- *
- */
-
-#define MAX_CPUS 16
-
-typedef struct CPUTimerState {
- qemu_irq irq;
- ptimer_state *timer;
- uint32_t count, counthigh, reached;
- /* processor only */
- uint32_t running;
- uint64_t limit;
-} CPUTimerState;
-
-typedef struct SLAVIO_TIMERState {
- SysBusDevice busdev;
- uint32_t num_cpus;
- uint32_t cputimer_mode;
- CPUTimerState cputimer[MAX_CPUS + 1];
-} SLAVIO_TIMERState;
-
-typedef struct TimerContext {
- MemoryRegion iomem;
- SLAVIO_TIMERState *s;
- unsigned int timer_index; /* 0 for system, 1 ... MAX_CPUS for CPU timers */
-} TimerContext;
-
-#define SYS_TIMER_SIZE 0x14
-#define CPU_TIMER_SIZE 0x10
-
-#define TIMER_LIMIT 0
-#define TIMER_COUNTER 1
-#define TIMER_COUNTER_NORST 2
-#define TIMER_STATUS 3
-#define TIMER_MODE 4
-
-#define TIMER_COUNT_MASK32 0xfffffe00
-#define TIMER_LIMIT_MASK32 0x7fffffff
-#define TIMER_MAX_COUNT64 0x7ffffffffffffe00ULL
-#define TIMER_MAX_COUNT32 0x7ffffe00ULL
-#define TIMER_REACHED 0x80000000
-#define TIMER_PERIOD 500ULL // 500ns
-#define LIMIT_TO_PERIODS(l) (((l) >> 9) - 1)
-#define PERIODS_TO_LIMIT(l) (((l) + 1) << 9)
-
-static int slavio_timer_is_user(TimerContext *tc)
-{
- SLAVIO_TIMERState *s = tc->s;
- unsigned int timer_index = tc->timer_index;
-
- return timer_index != 0 && (s->cputimer_mode & (1 << (timer_index - 1)));
-}
-
-// Update count, set irq, update expire_time
-// Convert from ptimer countdown units
-static void slavio_timer_get_out(CPUTimerState *t)
-{
- uint64_t count, limit;
-
- if (t->limit == 0) { /* free-run system or processor counter */
- limit = TIMER_MAX_COUNT32;
- } else {
- limit = t->limit;
- }
- count = limit - PERIODS_TO_LIMIT(ptimer_get_count(t->timer));
-
- trace_slavio_timer_get_out(t->limit, t->counthigh, t->count);
- t->count = count & TIMER_COUNT_MASK32;
- t->counthigh = count >> 32;
-}
-
-// timer callback
-static void slavio_timer_irq(void *opaque)
-{
- TimerContext *tc = opaque;
- SLAVIO_TIMERState *s = tc->s;
- CPUTimerState *t = &s->cputimer[tc->timer_index];
-
- slavio_timer_get_out(t);
- trace_slavio_timer_irq(t->counthigh, t->count);
- /* if limit is 0 (free-run), there will be no match */
- if (t->limit != 0) {
- t->reached = TIMER_REACHED;
- }
- /* there is no interrupt if user timer or free-run */
- if (!slavio_timer_is_user(tc) && t->limit != 0) {
- qemu_irq_raise(t->irq);
- }
-}
-
-static uint64_t slavio_timer_mem_readl(void *opaque, hwaddr addr,
- unsigned size)
-{
- TimerContext *tc = opaque;
- SLAVIO_TIMERState *s = tc->s;
- uint32_t saddr, ret;
- unsigned int timer_index = tc->timer_index;
- CPUTimerState *t = &s->cputimer[timer_index];
-
- saddr = addr >> 2;
- switch (saddr) {
- case TIMER_LIMIT:
- // read limit (system counter mode) or read most signifying
- // part of counter (user mode)
- if (slavio_timer_is_user(tc)) {
- // read user timer MSW
- slavio_timer_get_out(t);
- ret = t->counthigh | t->reached;
- } else {
- // read limit
- // clear irq
- qemu_irq_lower(t->irq);
- t->reached = 0;
- ret = t->limit & TIMER_LIMIT_MASK32;
- }
- break;
- case TIMER_COUNTER:
- // read counter and reached bit (system mode) or read lsbits
- // of counter (user mode)
- slavio_timer_get_out(t);
- if (slavio_timer_is_user(tc)) { // read user timer LSW
- ret = t->count & TIMER_MAX_COUNT64;
- } else { // read limit
- ret = (t->count & TIMER_MAX_COUNT32) |
- t->reached;
- }
- break;
- case TIMER_STATUS:
- // only available in processor counter/timer
- // read start/stop status
- if (timer_index > 0) {
- ret = t->running;
- } else {
- ret = 0;
- }
- break;
- case TIMER_MODE:
- // only available in system counter
- // read user/system mode
- ret = s->cputimer_mode;
- break;
- default:
- trace_slavio_timer_mem_readl_invalid(addr);
- ret = 0;
- break;
- }
- trace_slavio_timer_mem_readl(addr, ret);
- return ret;
-}
-
-static void slavio_timer_mem_writel(void *opaque, hwaddr addr,
- uint64_t val, unsigned size)
-{
- TimerContext *tc = opaque;
- SLAVIO_TIMERState *s = tc->s;
- uint32_t saddr;
- unsigned int timer_index = tc->timer_index;
- CPUTimerState *t = &s->cputimer[timer_index];
-
- trace_slavio_timer_mem_writel(addr, val);
- saddr = addr >> 2;
- switch (saddr) {
- case TIMER_LIMIT:
- if (slavio_timer_is_user(tc)) {
- uint64_t count;
-
- // set user counter MSW, reset counter
- t->limit = TIMER_MAX_COUNT64;
- t->counthigh = val & (TIMER_MAX_COUNT64 >> 32);
- t->reached = 0;
- count = ((uint64_t)t->counthigh << 32) | t->count;
- trace_slavio_timer_mem_writel_limit(timer_index, count);
- ptimer_set_count(t->timer, LIMIT_TO_PERIODS(t->limit - count));
- } else {
- // set limit, reset counter
- qemu_irq_lower(t->irq);
- t->limit = val & TIMER_MAX_COUNT32;
- if (t->timer) {
- if (t->limit == 0) { /* free-run */
- ptimer_set_limit(t->timer,
- LIMIT_TO_PERIODS(TIMER_MAX_COUNT32), 1);
- } else {
- ptimer_set_limit(t->timer, LIMIT_TO_PERIODS(t->limit), 1);
- }
- }
- }
- break;
- case TIMER_COUNTER:
- if (slavio_timer_is_user(tc)) {
- uint64_t count;
-
- // set user counter LSW, reset counter
- t->limit = TIMER_MAX_COUNT64;
- t->count = val & TIMER_MAX_COUNT64;
- t->reached = 0;
- count = ((uint64_t)t->counthigh) << 32 | t->count;
- trace_slavio_timer_mem_writel_limit(timer_index, count);
- ptimer_set_count(t->timer, LIMIT_TO_PERIODS(t->limit - count));
- } else {
- trace_slavio_timer_mem_writel_counter_invalid();
- }
- break;
- case TIMER_COUNTER_NORST:
- // set limit without resetting counter
- t->limit = val & TIMER_MAX_COUNT32;
- if (t->limit == 0) { /* free-run */
- ptimer_set_limit(t->timer, LIMIT_TO_PERIODS(TIMER_MAX_COUNT32), 0);
- } else {
- ptimer_set_limit(t->timer, LIMIT_TO_PERIODS(t->limit), 0);
- }
- break;
- case TIMER_STATUS:
- if (slavio_timer_is_user(tc)) {
- // start/stop user counter
- if ((val & 1) && !t->running) {
- trace_slavio_timer_mem_writel_status_start(timer_index);
- ptimer_run(t->timer, 0);
- t->running = 1;
- } else if (!(val & 1) && t->running) {
- trace_slavio_timer_mem_writel_status_stop(timer_index);
- ptimer_stop(t->timer);
- t->running = 0;
- }
- }
- break;
- case TIMER_MODE:
- if (timer_index == 0) {
- unsigned int i;
-
- for (i = 0; i < s->num_cpus; i++) {
- unsigned int processor = 1 << i;
- CPUTimerState *curr_timer = &s->cputimer[i + 1];
-
- // check for a change in timer mode for this processor
- if ((val & processor) != (s->cputimer_mode & processor)) {
- if (val & processor) { // counter -> user timer
- qemu_irq_lower(curr_timer->irq);
- // counters are always running
- ptimer_stop(curr_timer->timer);
- curr_timer->running = 0;
- // user timer limit is always the same
- curr_timer->limit = TIMER_MAX_COUNT64;
- ptimer_set_limit(curr_timer->timer,
- LIMIT_TO_PERIODS(curr_timer->limit),
- 1);
- // set this processors user timer bit in config
- // register
- s->cputimer_mode |= processor;
- trace_slavio_timer_mem_writel_mode_user(timer_index);
- } else { // user timer -> counter
- // stop the user timer if it is running
- if (curr_timer->running) {
- ptimer_stop(curr_timer->timer);
- }
- // start the counter
- ptimer_run(curr_timer->timer, 0);
- curr_timer->running = 1;
- // clear this processors user timer bit in config
- // register
- s->cputimer_mode &= ~processor;
- trace_slavio_timer_mem_writel_mode_counter(timer_index);
- }
- }
- }
- } else {
- trace_slavio_timer_mem_writel_mode_invalid();
- }
- break;
- default:
- trace_slavio_timer_mem_writel_invalid(addr);
- break;
- }
-}
-
-static const MemoryRegionOps slavio_timer_mem_ops = {
- .read = slavio_timer_mem_readl,
- .write = slavio_timer_mem_writel,
- .endianness = DEVICE_NATIVE_ENDIAN,
- .valid = {
- .min_access_size = 4,
- .max_access_size = 4,
- },
-};
-
-static const VMStateDescription vmstate_timer = {
- .name ="timer",
- .version_id = 3,
- .minimum_version_id = 3,
- .minimum_version_id_old = 3,
- .fields = (VMStateField []) {
- VMSTATE_UINT64(limit, CPUTimerState),
- VMSTATE_UINT32(count, CPUTimerState),
- VMSTATE_UINT32(counthigh, CPUTimerState),
- VMSTATE_UINT32(reached, CPUTimerState),
- VMSTATE_UINT32(running, CPUTimerState),
- VMSTATE_PTIMER(timer, CPUTimerState),
- VMSTATE_END_OF_LIST()
- }
-};
-
-static const VMStateDescription vmstate_slavio_timer = {
- .name ="slavio_timer",
- .version_id = 3,
- .minimum_version_id = 3,
- .minimum_version_id_old = 3,
- .fields = (VMStateField []) {
- VMSTATE_STRUCT_ARRAY(cputimer, SLAVIO_TIMERState, MAX_CPUS + 1, 3,
- vmstate_timer, CPUTimerState),
- VMSTATE_END_OF_LIST()
- }
-};
-
-static void slavio_timer_reset(DeviceState *d)
-{
- SLAVIO_TIMERState *s = container_of(d, SLAVIO_TIMERState, busdev.qdev);
- unsigned int i;
- CPUTimerState *curr_timer;
-
- for (i = 0; i <= MAX_CPUS; i++) {
- curr_timer = &s->cputimer[i];
- curr_timer->limit = 0;
- curr_timer->count = 0;
- curr_timer->reached = 0;
- if (i <= s->num_cpus) {
- ptimer_set_limit(curr_timer->timer,
- LIMIT_TO_PERIODS(TIMER_MAX_COUNT32), 1);
- ptimer_run(curr_timer->timer, 0);
- curr_timer->running = 1;
- }
- }
- s->cputimer_mode = 0;
-}
-
-static int slavio_timer_init1(SysBusDevice *dev)
-{
- SLAVIO_TIMERState *s = FROM_SYSBUS(SLAVIO_TIMERState, dev);
- QEMUBH *bh;
- unsigned int i;
- TimerContext *tc;
-
- for (i = 0; i <= MAX_CPUS; i++) {
- uint64_t size;
- char timer_name[20];
-
- tc = g_malloc0(sizeof(TimerContext));
- tc->s = s;
- tc->timer_index = i;
-
- bh = qemu_bh_new(slavio_timer_irq, tc);
- s->cputimer[i].timer = ptimer_init(bh);
- ptimer_set_period(s->cputimer[i].timer, TIMER_PERIOD);
-
- size = i == 0 ? SYS_TIMER_SIZE : CPU_TIMER_SIZE;
- snprintf(timer_name, sizeof(timer_name), "timer-%i", i);
- memory_region_init_io(&tc->iomem, &slavio_timer_mem_ops, tc,
- timer_name, size);
- sysbus_init_mmio(dev, &tc->iomem);
-
- sysbus_init_irq(dev, &s->cputimer[i].irq);
- }
-
- return 0;
-}
-
-static Property slavio_timer_properties[] = {
- DEFINE_PROP_UINT32("num_cpus", SLAVIO_TIMERState, num_cpus, 0),
- DEFINE_PROP_END_OF_LIST(),
-};
-
-static void slavio_timer_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-
- k->init = slavio_timer_init1;
- dc->reset = slavio_timer_reset;
- dc->vmsd = &vmstate_slavio_timer;
- dc->props = slavio_timer_properties;
-}
-
-static const TypeInfo slavio_timer_info = {
- .name = "slavio_timer",
- .parent = TYPE_SYS_BUS_DEVICE,
- .instance_size = sizeof(SLAVIO_TIMERState),
- .class_init = slavio_timer_class_init,
-};
-
-static void slavio_timer_register_types(void)
-{
- type_register_static(&slavio_timer_info);
-}
-
-type_init(slavio_timer_register_types)
obj-y = sun4m_iommu.o slavio_intctl.o
-obj-y += slavio_timer.o slavio_misc.o sparc32_dma.o
+obj-y += slavio_misc.o sparc32_dma.o
obj-y += eccmemctl.o sbi.o sun4c_intctl.o
# GRLIB
-obj-y += grlib_gptimer.o grlib_irqmp.o
+obj-y += grlib_irqmp.o
obj-y := $(addprefix ../,$(obj-y))
common-obj-$(CONFIG_PUV3) += puv3_ost.o
common-obj-$(CONFIG_TWL92230) += twl92230.o
common-obj-$(CONFIG_XILINX) += xilinx_timer.o
+common-obj-$(CONFIG_SLAVIO) += slavio_timer.o
+common-obj-$(CONFIG_ETRAXFS) += etraxfs_timer.o
+common-obj-$(CONFIG_GRLIB) += grlib_gptimer.o
+common-obj-$(CONFIG_IMX) += imx_timer.o
+common-obj-$(CONFIG_LM32) += lm32_timer.o
+common-obj-$(CONFIG_MILKYMIST) += milkymist-sysctl.o
+obj-$(CONFIG_EXYNOS4) += exynos4210_mct.o
+obj-$(CONFIG_EXYNOS4) += exynos4210_pwm.o
+obj-$(CONFIG_EXYNOS4) += exynos4210_rtc.o
+obj-$(CONFIG_OMAP) += omap_gptimer.o
+obj-$(CONFIG_OMAP) += omap_synctimer.o
+obj-$(CONFIG_PXA2XX) += pxa2xx_timer.o
+obj-$(CONFIG_SH4) += sh_timer.o
+obj-$(CONFIG_TUSB6010) += tusb6010.o
+
+obj-$(CONFIG_ARM_MPTIMER) += arm_mptimer.o
obj-$(CONFIG_MC146818RTC) += mc146818rtc.o
--- /dev/null
+/*
+ * Private peripheral timer/watchdog blocks for ARM 11MPCore and A9MP
+ *
+ * Copyright (c) 2006-2007 CodeSourcery.
+ * Copyright (c) 2011 Linaro Limited
+ * Written by Paul Brook, Peter Maydell
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "hw/sysbus.h"
+#include "qemu/timer.h"
+
+/* This device implements the per-cpu private timer and watchdog block
+ * which is used in both the ARM11MPCore and Cortex-A9MP.
+ */
+
+#define MAX_CPUS 4
+
+/* State of a single timer or watchdog block */
+typedef struct {
+ uint32_t count;
+ uint32_t load;
+ uint32_t control;
+ uint32_t status;
+ int64_t tick;
+ QEMUTimer *timer;
+ qemu_irq irq;
+ MemoryRegion iomem;
+} TimerBlock;
+
+typedef struct {
+ SysBusDevice busdev;
+ uint32_t num_cpu;
+ TimerBlock timerblock[MAX_CPUS];
+ MemoryRegion iomem;
+} ARMMPTimerState;
+
+static inline int get_current_cpu(ARMMPTimerState *s)
+{
+ CPUState *cpu_single_cpu = ENV_GET_CPU(cpu_single_env);
+
+ if (cpu_single_cpu->cpu_index >= s->num_cpu) {
+ hw_error("arm_mptimer: num-cpu %d but this cpu is %d!\n",
+ s->num_cpu, cpu_single_cpu->cpu_index);
+ }
+ return cpu_single_cpu->cpu_index;
+}
+
+static inline void timerblock_update_irq(TimerBlock *tb)
+{
+ qemu_set_irq(tb->irq, tb->status);
+}
+
+/* Return conversion factor from mpcore timer ticks to qemu timer ticks. */
+static inline uint32_t timerblock_scale(TimerBlock *tb)
+{
+ return (((tb->control >> 8) & 0xff) + 1) * 10;
+}
+
+static void timerblock_reload(TimerBlock *tb, int restart)
+{
+ if (tb->count == 0) {
+ return;
+ }
+ if (restart) {
+ tb->tick = qemu_get_clock_ns(vm_clock);
+ }
+ tb->tick += (int64_t)tb->count * timerblock_scale(tb);
+ qemu_mod_timer(tb->timer, tb->tick);
+}
+
+static void timerblock_tick(void *opaque)
+{
+ TimerBlock *tb = (TimerBlock *)opaque;
+ tb->status = 1;
+ if (tb->control & 2) {
+ tb->count = tb->load;
+ timerblock_reload(tb, 0);
+ } else {
+ tb->count = 0;
+ }
+ timerblock_update_irq(tb);
+}
+
+static uint64_t timerblock_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ TimerBlock *tb = (TimerBlock *)opaque;
+ int64_t val;
+ switch (addr) {
+ case 0: /* Load */
+ return tb->load;
+ case 4: /* Counter. */
+ if (((tb->control & 1) == 0) || (tb->count == 0)) {
+ return 0;
+ }
+ /* Slow and ugly, but hopefully won't happen too often. */
+ val = tb->tick - qemu_get_clock_ns(vm_clock);
+ val /= timerblock_scale(tb);
+ if (val < 0) {
+ val = 0;
+ }
+ return val;
+ case 8: /* Control. */
+ return tb->control;
+ case 12: /* Interrupt status. */
+ return tb->status;
+ default:
+ return 0;
+ }
+}
+
+static void timerblock_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ TimerBlock *tb = (TimerBlock *)opaque;
+ int64_t old;
+ switch (addr) {
+ case 0: /* Load */
+ tb->load = value;
+ /* Fall through. */
+ case 4: /* Counter. */
+ if ((tb->control & 1) && tb->count) {
+ /* Cancel the previous timer. */
+ qemu_del_timer(tb->timer);
+ }
+ tb->count = value;
+ if (tb->control & 1) {
+ timerblock_reload(tb, 1);
+ }
+ break;
+ case 8: /* Control. */
+ old = tb->control;
+ tb->control = value;
+ if (((old & 1) == 0) && (value & 1)) {
+ if (tb->count == 0 && (tb->control & 2)) {
+ tb->count = tb->load;
+ }
+ timerblock_reload(tb, 1);
+ }
+ break;
+ case 12: /* Interrupt status. */
+ tb->status &= ~value;
+ timerblock_update_irq(tb);
+ break;
+ }
+}
+
+/* Wrapper functions to implement the "read timer/watchdog for
+ * the current CPU" memory regions.
+ */
+static uint64_t arm_thistimer_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ ARMMPTimerState *s = (ARMMPTimerState *)opaque;
+ int id = get_current_cpu(s);
+ return timerblock_read(&s->timerblock[id], addr, size);
+}
+
+static void arm_thistimer_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ ARMMPTimerState *s = (ARMMPTimerState *)opaque;
+ int id = get_current_cpu(s);
+ timerblock_write(&s->timerblock[id], addr, value, size);
+}
+
+static const MemoryRegionOps arm_thistimer_ops = {
+ .read = arm_thistimer_read,
+ .write = arm_thistimer_write,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static const MemoryRegionOps timerblock_ops = {
+ .read = timerblock_read,
+ .write = timerblock_write,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void timerblock_reset(TimerBlock *tb)
+{
+ tb->count = 0;
+ tb->load = 0;
+ tb->control = 0;
+ tb->status = 0;
+ tb->tick = 0;
+ if (tb->timer) {
+ qemu_del_timer(tb->timer);
+ }
+}
+
+static void arm_mptimer_reset(DeviceState *dev)
+{
+ ARMMPTimerState *s =
+ FROM_SYSBUS(ARMMPTimerState, SYS_BUS_DEVICE(dev));
+ int i;
+ for (i = 0; i < ARRAY_SIZE(s->timerblock); i++) {
+ timerblock_reset(&s->timerblock[i]);
+ }
+}
+
+static int arm_mptimer_init(SysBusDevice *dev)
+{
+ ARMMPTimerState *s = FROM_SYSBUS(ARMMPTimerState, dev);
+ int i;
+ if (s->num_cpu < 1 || s->num_cpu > MAX_CPUS) {
+ hw_error("%s: num-cpu must be between 1 and %d\n", __func__, MAX_CPUS);
+ }
+ /* We implement one timer block per CPU, and expose multiple MMIO regions:
+ * * region 0 is "timer for this core"
+ * * region 1 is "timer for core 0"
+ * * region 2 is "timer for core 1"
+ * and so on.
+ * The outgoing interrupt lines are
+ * * timer for core 0
+ * * timer for core 1
+ * and so on.
+ */
+ memory_region_init_io(&s->iomem, &arm_thistimer_ops, s,
+ "arm_mptimer_timer", 0x20);
+ sysbus_init_mmio(dev, &s->iomem);
+ for (i = 0; i < s->num_cpu; i++) {
+ TimerBlock *tb = &s->timerblock[i];
+ tb->timer = qemu_new_timer_ns(vm_clock, timerblock_tick, tb);
+ sysbus_init_irq(dev, &tb->irq);
+ memory_region_init_io(&tb->iomem, &timerblock_ops, tb,
+ "arm_mptimer_timerblock", 0x20);
+ sysbus_init_mmio(dev, &tb->iomem);
+ }
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_timerblock = {
+ .name = "arm_mptimer_timerblock",
+ .version_id = 2,
+ .minimum_version_id = 2,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(count, TimerBlock),
+ VMSTATE_UINT32(load, TimerBlock),
+ VMSTATE_UINT32(control, TimerBlock),
+ VMSTATE_UINT32(status, TimerBlock),
+ VMSTATE_INT64(tick, TimerBlock),
+ VMSTATE_TIMER(timer, TimerBlock),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_arm_mptimer = {
+ .name = "arm_mptimer",
+ .version_id = 2,
+ .minimum_version_id = 2,
+ .fields = (VMStateField[]) {
+ VMSTATE_STRUCT_VARRAY_UINT32(timerblock, ARMMPTimerState, num_cpu,
+ 2, vmstate_timerblock, TimerBlock),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static Property arm_mptimer_properties[] = {
+ DEFINE_PROP_UINT32("num-cpu", ARMMPTimerState, num_cpu, 0),
+ DEFINE_PROP_END_OF_LIST()
+};
+
+static void arm_mptimer_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass);
+
+ sbc->init = arm_mptimer_init;
+ dc->vmsd = &vmstate_arm_mptimer;
+ dc->reset = arm_mptimer_reset;
+ dc->no_user = 1;
+ dc->props = arm_mptimer_properties;
+}
+
+static const TypeInfo arm_mptimer_info = {
+ .name = "arm_mptimer",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(ARMMPTimerState),
+ .class_init = arm_mptimer_class_init,
+};
+
+static void arm_mptimer_register_types(void)
+{
+ type_register_static(&arm_mptimer_info);
+}
+
+type_init(arm_mptimer_register_types)
--- /dev/null
+/*
+ * QEMU ETRAX Timers
+ *
+ * Copyright (c) 2007 Edgar E. Iglesias, Axis Communications AB.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw/sysbus.h"
+#include "sysemu/sysemu.h"
+#include "qemu/timer.h"
+#include "hw/ptimer.h"
+
+#define D(x)
+
+#define RW_TMR0_DIV 0x00
+#define R_TMR0_DATA 0x04
+#define RW_TMR0_CTRL 0x08
+#define RW_TMR1_DIV 0x10
+#define R_TMR1_DATA 0x14
+#define RW_TMR1_CTRL 0x18
+#define R_TIME 0x38
+#define RW_WD_CTRL 0x40
+#define R_WD_STAT 0x44
+#define RW_INTR_MASK 0x48
+#define RW_ACK_INTR 0x4c
+#define R_INTR 0x50
+#define R_MASKED_INTR 0x54
+
+struct etrax_timer {
+ SysBusDevice busdev;
+ MemoryRegion mmio;
+ qemu_irq irq;
+ qemu_irq nmi;
+
+ QEMUBH *bh_t0;
+ QEMUBH *bh_t1;
+ QEMUBH *bh_wd;
+ ptimer_state *ptimer_t0;
+ ptimer_state *ptimer_t1;
+ ptimer_state *ptimer_wd;
+
+ int wd_hits;
+
+ /* Control registers. */
+ uint32_t rw_tmr0_div;
+ uint32_t r_tmr0_data;
+ uint32_t rw_tmr0_ctrl;
+
+ uint32_t rw_tmr1_div;
+ uint32_t r_tmr1_data;
+ uint32_t rw_tmr1_ctrl;
+
+ uint32_t rw_wd_ctrl;
+
+ uint32_t rw_intr_mask;
+ uint32_t rw_ack_intr;
+ uint32_t r_intr;
+ uint32_t r_masked_intr;
+};
+
+static uint64_t
+timer_read(void *opaque, hwaddr addr, unsigned int size)
+{
+ struct etrax_timer *t = opaque;
+ uint32_t r = 0;
+
+ switch (addr) {
+ case R_TMR0_DATA:
+ r = ptimer_get_count(t->ptimer_t0);
+ break;
+ case R_TMR1_DATA:
+ r = ptimer_get_count(t->ptimer_t1);
+ break;
+ case R_TIME:
+ r = qemu_get_clock_ns(vm_clock) / 10;
+ break;
+ case RW_INTR_MASK:
+ r = t->rw_intr_mask;
+ break;
+ case R_MASKED_INTR:
+ r = t->r_intr & t->rw_intr_mask;
+ break;
+ default:
+ D(printf ("%s %x\n", __func__, addr));
+ break;
+ }
+ return r;
+}
+
+static void update_ctrl(struct etrax_timer *t, int tnum)
+{
+ unsigned int op;
+ unsigned int freq;
+ unsigned int freq_hz;
+ unsigned int div;
+ uint32_t ctrl;
+
+ ptimer_state *timer;
+
+ if (tnum == 0) {
+ ctrl = t->rw_tmr0_ctrl;
+ div = t->rw_tmr0_div;
+ timer = t->ptimer_t0;
+ } else {
+ ctrl = t->rw_tmr1_ctrl;
+ div = t->rw_tmr1_div;
+ timer = t->ptimer_t1;
+ }
+
+
+ op = ctrl & 3;
+ freq = ctrl >> 2;
+ freq_hz = 32000000;
+
+ switch (freq)
+ {
+ case 0:
+ case 1:
+ D(printf ("extern or disabled timer clock?\n"));
+ break;
+ case 4: freq_hz = 29493000; break;
+ case 5: freq_hz = 32000000; break;
+ case 6: freq_hz = 32768000; break;
+ case 7: freq_hz = 100000000; break;
+ default:
+ abort();
+ break;
+ }
+
+ D(printf ("freq_hz=%d div=%d\n", freq_hz, div));
+ ptimer_set_freq(timer, freq_hz);
+ ptimer_set_limit(timer, div, 0);
+
+ switch (op)
+ {
+ case 0:
+ /* Load. */
+ ptimer_set_limit(timer, div, 1);
+ break;
+ case 1:
+ /* Hold. */
+ ptimer_stop(timer);
+ break;
+ case 2:
+ /* Run. */
+ ptimer_run(timer, 0);
+ break;
+ default:
+ abort();
+ break;
+ }
+}
+
+static void timer_update_irq(struct etrax_timer *t)
+{
+ t->r_intr &= ~(t->rw_ack_intr);
+ t->r_masked_intr = t->r_intr & t->rw_intr_mask;
+
+ D(printf("%s: masked_intr=%x\n", __func__, t->r_masked_intr));
+ qemu_set_irq(t->irq, !!t->r_masked_intr);
+}
+
+static void timer0_hit(void *opaque)
+{
+ struct etrax_timer *t = opaque;
+ t->r_intr |= 1;
+ timer_update_irq(t);
+}
+
+static void timer1_hit(void *opaque)
+{
+ struct etrax_timer *t = opaque;
+ t->r_intr |= 2;
+ timer_update_irq(t);
+}
+
+static void watchdog_hit(void *opaque)
+{
+ struct etrax_timer *t = opaque;
+ if (t->wd_hits == 0) {
+ /* real hw gives a single tick before reseting but we are
+ a bit friendlier to compensate for our slower execution. */
+ ptimer_set_count(t->ptimer_wd, 10);
+ ptimer_run(t->ptimer_wd, 1);
+ qemu_irq_raise(t->nmi);
+ }
+ else
+ qemu_system_reset_request();
+
+ t->wd_hits++;
+}
+
+static inline void timer_watchdog_update(struct etrax_timer *t, uint32_t value)
+{
+ unsigned int wd_en = t->rw_wd_ctrl & (1 << 8);
+ unsigned int wd_key = t->rw_wd_ctrl >> 9;
+ unsigned int wd_cnt = t->rw_wd_ctrl & 511;
+ unsigned int new_key = value >> 9 & ((1 << 7) - 1);
+ unsigned int new_cmd = (value >> 8) & 1;
+
+ /* If the watchdog is enabled, they written key must match the
+ complement of the previous. */
+ wd_key = ~wd_key & ((1 << 7) - 1);
+
+ if (wd_en && wd_key != new_key)
+ return;
+
+ D(printf("en=%d new_key=%x oldkey=%x cmd=%d cnt=%d\n",
+ wd_en, new_key, wd_key, new_cmd, wd_cnt));
+
+ if (t->wd_hits)
+ qemu_irq_lower(t->nmi);
+
+ t->wd_hits = 0;
+
+ ptimer_set_freq(t->ptimer_wd, 760);
+ if (wd_cnt == 0)
+ wd_cnt = 256;
+ ptimer_set_count(t->ptimer_wd, wd_cnt);
+ if (new_cmd)
+ ptimer_run(t->ptimer_wd, 1);
+ else
+ ptimer_stop(t->ptimer_wd);
+
+ t->rw_wd_ctrl = value;
+}
+
+static void
+timer_write(void *opaque, hwaddr addr,
+ uint64_t val64, unsigned int size)
+{
+ struct etrax_timer *t = opaque;
+ uint32_t value = val64;
+
+ switch (addr)
+ {
+ case RW_TMR0_DIV:
+ t->rw_tmr0_div = value;
+ break;
+ case RW_TMR0_CTRL:
+ D(printf ("RW_TMR0_CTRL=%x\n", value));
+ t->rw_tmr0_ctrl = value;
+ update_ctrl(t, 0);
+ break;
+ case RW_TMR1_DIV:
+ t->rw_tmr1_div = value;
+ break;
+ case RW_TMR1_CTRL:
+ D(printf ("RW_TMR1_CTRL=%x\n", value));
+ t->rw_tmr1_ctrl = value;
+ update_ctrl(t, 1);
+ break;
+ case RW_INTR_MASK:
+ D(printf ("RW_INTR_MASK=%x\n", value));
+ t->rw_intr_mask = value;
+ timer_update_irq(t);
+ break;
+ case RW_WD_CTRL:
+ timer_watchdog_update(t, value);
+ break;
+ case RW_ACK_INTR:
+ t->rw_ack_intr = value;
+ timer_update_irq(t);
+ t->rw_ack_intr = 0;
+ break;
+ default:
+ printf ("%s " TARGET_FMT_plx " %x\n",
+ __func__, addr, value);
+ break;
+ }
+}
+
+static const MemoryRegionOps timer_ops = {
+ .read = timer_read,
+ .write = timer_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4
+ }
+};
+
+static void etraxfs_timer_reset(void *opaque)
+{
+ struct etrax_timer *t = opaque;
+
+ ptimer_stop(t->ptimer_t0);
+ ptimer_stop(t->ptimer_t1);
+ ptimer_stop(t->ptimer_wd);
+ t->rw_wd_ctrl = 0;
+ t->r_intr = 0;
+ t->rw_intr_mask = 0;
+ qemu_irq_lower(t->irq);
+}
+
+static int etraxfs_timer_init(SysBusDevice *dev)
+{
+ struct etrax_timer *t = FROM_SYSBUS(typeof (*t), dev);
+
+ t->bh_t0 = qemu_bh_new(timer0_hit, t);
+ t->bh_t1 = qemu_bh_new(timer1_hit, t);
+ t->bh_wd = qemu_bh_new(watchdog_hit, t);
+ t->ptimer_t0 = ptimer_init(t->bh_t0);
+ t->ptimer_t1 = ptimer_init(t->bh_t1);
+ t->ptimer_wd = ptimer_init(t->bh_wd);
+
+ sysbus_init_irq(dev, &t->irq);
+ sysbus_init_irq(dev, &t->nmi);
+
+ memory_region_init_io(&t->mmio, &timer_ops, t, "etraxfs-timer", 0x5c);
+ sysbus_init_mmio(dev, &t->mmio);
+ qemu_register_reset(etraxfs_timer_reset, t);
+ return 0;
+}
+
+static void etraxfs_timer_class_init(ObjectClass *klass, void *data)
+{
+ SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+
+ sdc->init = etraxfs_timer_init;
+}
+
+static const TypeInfo etraxfs_timer_info = {
+ .name = "etraxfs,timer",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof (struct etrax_timer),
+ .class_init = etraxfs_timer_class_init,
+};
+
+static void etraxfs_timer_register_types(void)
+{
+ type_register_static(&etraxfs_timer_info);
+}
+
+type_init(etraxfs_timer_register_types)
--- /dev/null
+/*
+ * Samsung exynos4210 Multi Core timer
+ *
+ * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd.
+ * All rights reserved.
+ *
+ * Evgeny Voevodin <e.voevodin@samsung.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 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 <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Global Timer:
+ *
+ * Consists of two timers. First represents Free Running Counter and second
+ * is used to measure interval from FRC to nearest comparator.
+ *
+ * 0 UINT64_MAX
+ * | timer0 |
+ * | <-------------------------------------------------------------- |
+ * | --------------------------------------------frc---------------> |
+ * |______________________________________________|__________________|
+ * CMP0 CMP1 CMP2 | CMP3
+ * __| |_
+ * | timer1 |
+ * | -------------> |
+ * frc CMPx
+ *
+ * Problem: when implementing global timer as is, overflow arises.
+ * next_time = cur_time + period * count;
+ * period and count are 64 bits width.
+ * Lets arm timer for MCT_GT_COUNTER_STEP count and update internal G_CNT
+ * register during each event.
+ *
+ * Problem: both timers need to be implemented using MCT_XT_COUNTER_STEP because
+ * local timer contains two counters: TCNT and ICNT. TCNT == 0 -> ICNT--.
+ * IRQ is generated when ICNT riches zero. Implementation where TCNT == 0
+ * generates IRQs suffers from too frequently events. Better to have one
+ * uint64_t counter equal to TCNT*ICNT and arm ptimer.c for a minimum(TCNT*ICNT,
+ * MCT_GT_COUNTER_STEP); (yes, if target tunes ICNT * TCNT to be too low values,
+ * there is no way to avoid frequently events).
+ */
+
+#include "hw/sysbus.h"
+#include "qemu/timer.h"
+#include "qemu-common.h"
+#include "hw/ptimer.h"
+
+#include "hw/arm/exynos4210.h"
+
+//#define DEBUG_MCT
+
+#ifdef DEBUG_MCT
+#define DPRINTF(fmt, ...) \
+ do { fprintf(stdout, "MCT: [%24s:%5d] " fmt, __func__, __LINE__, \
+ ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while (0)
+#endif
+
+#define MCT_CFG 0x000
+#define G_CNT_L 0x100
+#define G_CNT_U 0x104
+#define G_CNT_WSTAT 0x110
+#define G_COMP0_L 0x200
+#define G_COMP0_U 0x204
+#define G_COMP0_ADD_INCR 0x208
+#define G_COMP1_L 0x210
+#define G_COMP1_U 0x214
+#define G_COMP1_ADD_INCR 0x218
+#define G_COMP2_L 0x220
+#define G_COMP2_U 0x224
+#define G_COMP2_ADD_INCR 0x228
+#define G_COMP3_L 0x230
+#define G_COMP3_U 0x234
+#define G_COMP3_ADD_INCR 0x238
+#define G_TCON 0x240
+#define G_INT_CSTAT 0x244
+#define G_INT_ENB 0x248
+#define G_WSTAT 0x24C
+#define L0_TCNTB 0x300
+#define L0_TCNTO 0x304
+#define L0_ICNTB 0x308
+#define L0_ICNTO 0x30C
+#define L0_FRCNTB 0x310
+#define L0_FRCNTO 0x314
+#define L0_TCON 0x320
+#define L0_INT_CSTAT 0x330
+#define L0_INT_ENB 0x334
+#define L0_WSTAT 0x340
+#define L1_TCNTB 0x400
+#define L1_TCNTO 0x404
+#define L1_ICNTB 0x408
+#define L1_ICNTO 0x40C
+#define L1_FRCNTB 0x410
+#define L1_FRCNTO 0x414
+#define L1_TCON 0x420
+#define L1_INT_CSTAT 0x430
+#define L1_INT_ENB 0x434
+#define L1_WSTAT 0x440
+
+#define MCT_CFG_GET_PRESCALER(x) ((x) & 0xFF)
+#define MCT_CFG_GET_DIVIDER(x) (1 << ((x) >> 8 & 7))
+
+#define GET_G_COMP_IDX(offset) (((offset) - G_COMP0_L) / 0x10)
+#define GET_G_COMP_ADD_INCR_IDX(offset) (((offset) - G_COMP0_ADD_INCR) / 0x10)
+
+#define G_COMP_L(x) (G_COMP0_L + (x) * 0x10)
+#define G_COMP_U(x) (G_COMP0_U + (x) * 0x10)
+
+#define G_COMP_ADD_INCR(x) (G_COMP0_ADD_INCR + (x) * 0x10)
+
+/* MCT bits */
+#define G_TCON_COMP_ENABLE(x) (1 << 2 * (x))
+#define G_TCON_AUTO_ICREMENT(x) (1 << (2 * (x) + 1))
+#define G_TCON_TIMER_ENABLE (1 << 8)
+
+#define G_INT_ENABLE(x) (1 << (x))
+#define G_INT_CSTAT_COMP(x) (1 << (x))
+
+#define G_CNT_WSTAT_L 1
+#define G_CNT_WSTAT_U 2
+
+#define G_WSTAT_COMP_L(x) (1 << 4 * (x))
+#define G_WSTAT_COMP_U(x) (1 << ((4 * (x)) + 1))
+#define G_WSTAT_COMP_ADDINCR(x) (1 << ((4 * (x)) + 2))
+#define G_WSTAT_TCON_WRITE (1 << 16)
+
+#define GET_L_TIMER_IDX(offset) ((((offset) & 0xF00) - L0_TCNTB) / 0x100)
+#define GET_L_TIMER_CNT_REG_IDX(offset, lt_i) \
+ (((offset) - (L0_TCNTB + 0x100 * (lt_i))) >> 2)
+
+#define L_ICNTB_MANUAL_UPDATE (1 << 31)
+
+#define L_TCON_TICK_START (1)
+#define L_TCON_INT_START (1 << 1)
+#define L_TCON_INTERVAL_MODE (1 << 2)
+#define L_TCON_FRC_START (1 << 3)
+
+#define L_INT_CSTAT_INTCNT (1 << 0)
+#define L_INT_CSTAT_FRCCNT (1 << 1)
+
+#define L_INT_INTENB_ICNTEIE (1 << 0)
+#define L_INT_INTENB_FRCEIE (1 << 1)
+
+#define L_WSTAT_TCNTB_WRITE (1 << 0)
+#define L_WSTAT_ICNTB_WRITE (1 << 1)
+#define L_WSTAT_FRCCNTB_WRITE (1 << 2)
+#define L_WSTAT_TCON_WRITE (1 << 3)
+
+enum LocalTimerRegCntIndexes {
+ L_REG_CNT_TCNTB,
+ L_REG_CNT_TCNTO,
+ L_REG_CNT_ICNTB,
+ L_REG_CNT_ICNTO,
+ L_REG_CNT_FRCCNTB,
+ L_REG_CNT_FRCCNTO,
+
+ L_REG_CNT_AMOUNT
+};
+
+#define MCT_NIRQ 6
+#define MCT_SFR_SIZE 0x444
+
+#define MCT_GT_CMP_NUM 4
+
+#define MCT_GT_MAX_VAL UINT64_MAX
+
+#define MCT_GT_COUNTER_STEP 0x100000000ULL
+#define MCT_LT_COUNTER_STEP 0x100000000ULL
+#define MCT_LT_CNT_LOW_LIMIT 0x100
+
+/* global timer */
+typedef struct {
+ qemu_irq irq[MCT_GT_CMP_NUM];
+
+ struct gregs {
+ uint64_t cnt;
+ uint32_t cnt_wstat;
+ uint32_t tcon;
+ uint32_t int_cstat;
+ uint32_t int_enb;
+ uint32_t wstat;
+ uint64_t comp[MCT_GT_CMP_NUM];
+ uint32_t comp_add_incr[MCT_GT_CMP_NUM];
+ } reg;
+
+ uint64_t count; /* Value FRC was armed with */
+ int32_t curr_comp; /* Current comparator FRC is running to */
+
+ ptimer_state *ptimer_frc; /* FRC timer */
+
+} Exynos4210MCTGT;
+
+/* local timer */
+typedef struct {
+ int id; /* timer id */
+ qemu_irq irq; /* local timer irq */
+
+ struct tick_timer {
+ uint32_t cnt_run; /* cnt timer is running */
+ uint32_t int_run; /* int timer is running */
+
+ uint32_t last_icnto;
+ uint32_t last_tcnto;
+ uint32_t tcntb; /* initial value for TCNTB */
+ uint32_t icntb; /* initial value for ICNTB */
+
+ /* for step mode */
+ uint64_t distance; /* distance to count to the next event */
+ uint64_t progress; /* progress when counting by steps */
+ uint64_t count; /* count to arm timer with */
+
+ ptimer_state *ptimer_tick; /* timer for tick counter */
+ } tick_timer;
+
+ /* use ptimer.c to represent count down timer */
+
+ ptimer_state *ptimer_frc; /* timer for free running counter */
+
+ /* registers */
+ struct lregs {
+ uint32_t cnt[L_REG_CNT_AMOUNT];
+ uint32_t tcon;
+ uint32_t int_cstat;
+ uint32_t int_enb;
+ uint32_t wstat;
+ } reg;
+
+} Exynos4210MCTLT;
+
+typedef struct Exynos4210MCTState {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+
+ /* Registers */
+ uint32_t reg_mct_cfg;
+
+ Exynos4210MCTLT l_timer[2];
+ Exynos4210MCTGT g_timer;
+
+ uint32_t freq; /* all timers tick frequency, TCLK */
+} Exynos4210MCTState;
+
+/*** VMState ***/
+static const VMStateDescription vmstate_tick_timer = {
+ .name = "exynos4210.mct.tick_timer",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(cnt_run, struct tick_timer),
+ VMSTATE_UINT32(int_run, struct tick_timer),
+ VMSTATE_UINT32(last_icnto, struct tick_timer),
+ VMSTATE_UINT32(last_tcnto, struct tick_timer),
+ VMSTATE_UINT32(tcntb, struct tick_timer),
+ VMSTATE_UINT32(icntb, struct tick_timer),
+ VMSTATE_UINT64(distance, struct tick_timer),
+ VMSTATE_UINT64(progress, struct tick_timer),
+ VMSTATE_UINT64(count, struct tick_timer),
+ VMSTATE_PTIMER(ptimer_tick, struct tick_timer),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_lregs = {
+ .name = "exynos4210.mct.lregs",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(cnt, struct lregs, L_REG_CNT_AMOUNT),
+ VMSTATE_UINT32(tcon, struct lregs),
+ VMSTATE_UINT32(int_cstat, struct lregs),
+ VMSTATE_UINT32(int_enb, struct lregs),
+ VMSTATE_UINT32(wstat, struct lregs),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_exynos4210_mct_lt = {
+ .name = "exynos4210.mct.lt",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_INT32(id, Exynos4210MCTLT),
+ VMSTATE_STRUCT(tick_timer, Exynos4210MCTLT, 0,
+ vmstate_tick_timer,
+ struct tick_timer),
+ VMSTATE_PTIMER(ptimer_frc, Exynos4210MCTLT),
+ VMSTATE_STRUCT(reg, Exynos4210MCTLT, 0,
+ vmstate_lregs,
+ struct lregs),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_gregs = {
+ .name = "exynos4210.mct.lregs",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT64(cnt, struct gregs),
+ VMSTATE_UINT32(cnt_wstat, struct gregs),
+ VMSTATE_UINT32(tcon, struct gregs),
+ VMSTATE_UINT32(int_cstat, struct gregs),
+ VMSTATE_UINT32(int_enb, struct gregs),
+ VMSTATE_UINT32(wstat, struct gregs),
+ VMSTATE_UINT64_ARRAY(comp, struct gregs, MCT_GT_CMP_NUM),
+ VMSTATE_UINT32_ARRAY(comp_add_incr, struct gregs,
+ MCT_GT_CMP_NUM),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_exynos4210_mct_gt = {
+ .name = "exynos4210.mct.lt",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_STRUCT(reg, Exynos4210MCTGT, 0, vmstate_gregs,
+ struct gregs),
+ VMSTATE_UINT64(count, Exynos4210MCTGT),
+ VMSTATE_INT32(curr_comp, Exynos4210MCTGT),
+ VMSTATE_PTIMER(ptimer_frc, Exynos4210MCTGT),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_exynos4210_mct_state = {
+ .name = "exynos4210.mct",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(reg_mct_cfg, Exynos4210MCTState),
+ VMSTATE_STRUCT_ARRAY(l_timer, Exynos4210MCTState, 2, 0,
+ vmstate_exynos4210_mct_lt, Exynos4210MCTLT),
+ VMSTATE_STRUCT(g_timer, Exynos4210MCTState, 0,
+ vmstate_exynos4210_mct_gt, Exynos4210MCTGT),
+ VMSTATE_UINT32(freq, Exynos4210MCTState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void exynos4210_mct_update_freq(Exynos4210MCTState *s);
+
+/*
+ * Set counter of FRC global timer.
+ */
+static void exynos4210_gfrc_set_count(Exynos4210MCTGT *s, uint64_t count)
+{
+ s->count = count;
+ DPRINTF("global timer frc set count 0x%llx\n", count);
+ ptimer_set_count(s->ptimer_frc, count);
+}
+
+/*
+ * Get counter of FRC global timer.
+ */
+static uint64_t exynos4210_gfrc_get_count(Exynos4210MCTGT *s)
+{
+ uint64_t count = 0;
+ count = ptimer_get_count(s->ptimer_frc);
+ count = s->count - count;
+ return s->reg.cnt + count;
+}
+
+/*
+ * Stop global FRC timer
+ */
+static void exynos4210_gfrc_stop(Exynos4210MCTGT *s)
+{
+ DPRINTF("global timer frc stop\n");
+
+ ptimer_stop(s->ptimer_frc);
+}
+
+/*
+ * Start global FRC timer
+ */
+static void exynos4210_gfrc_start(Exynos4210MCTGT *s)
+{
+ DPRINTF("global timer frc start\n");
+
+ ptimer_run(s->ptimer_frc, 1);
+}
+
+/*
+ * Find next nearest Comparator. If current Comparator value equals to other
+ * Comparator value, skip them both
+ */
+static int32_t exynos4210_gcomp_find(Exynos4210MCTState *s)
+{
+ int res;
+ int i;
+ int enabled;
+ uint64_t min;
+ int min_comp_i;
+ uint64_t gfrc;
+ uint64_t distance;
+ uint64_t distance_min;
+ int comp_i;
+
+ /* get gfrc count */
+ gfrc = exynos4210_gfrc_get_count(&s->g_timer);
+
+ min = UINT64_MAX;
+ distance_min = UINT64_MAX;
+ comp_i = MCT_GT_CMP_NUM;
+ min_comp_i = MCT_GT_CMP_NUM;
+ enabled = 0;
+
+ /* lookup for nearest comparator */
+ for (i = 0; i < MCT_GT_CMP_NUM; i++) {
+
+ if (s->g_timer.reg.tcon & G_TCON_COMP_ENABLE(i)) {
+
+ enabled = 1;
+
+ if (s->g_timer.reg.comp[i] > gfrc) {
+ /* Comparator is upper then FRC */
+ distance = s->g_timer.reg.comp[i] - gfrc;
+
+ if (distance <= distance_min) {
+ distance_min = distance;
+ comp_i = i;
+ }
+ } else {
+ /* Comparator is below FRC, find the smallest */
+
+ if (s->g_timer.reg.comp[i] <= min) {
+ min = s->g_timer.reg.comp[i];
+ min_comp_i = i;
+ }
+ }
+ }
+ }
+
+ if (!enabled) {
+ /* All Comparators disabled */
+ res = -1;
+ } else if (comp_i < MCT_GT_CMP_NUM) {
+ /* Found upper Comparator */
+ res = comp_i;
+ } else {
+ /* All Comparators are below or equal to FRC */
+ res = min_comp_i;
+ }
+
+ DPRINTF("found comparator %d: comp 0x%llx distance 0x%llx, gfrc 0x%llx\n",
+ res,
+ s->g_timer.reg.comp[res],
+ distance_min,
+ gfrc);
+
+ return res;
+}
+
+/*
+ * Get distance to nearest Comparator
+ */
+static uint64_t exynos4210_gcomp_get_distance(Exynos4210MCTState *s, int32_t id)
+{
+ if (id == -1) {
+ /* no enabled Comparators, choose max distance */
+ return MCT_GT_COUNTER_STEP;
+ }
+ if (s->g_timer.reg.comp[id] - s->g_timer.reg.cnt < MCT_GT_COUNTER_STEP) {
+ return s->g_timer.reg.comp[id] - s->g_timer.reg.cnt;
+ } else {
+ return MCT_GT_COUNTER_STEP;
+ }
+}
+
+/*
+ * Restart global FRC timer
+ */
+static void exynos4210_gfrc_restart(Exynos4210MCTState *s)
+{
+ uint64_t distance;
+
+ exynos4210_gfrc_stop(&s->g_timer);
+
+ s->g_timer.curr_comp = exynos4210_gcomp_find(s);
+
+ distance = exynos4210_gcomp_get_distance(s, s->g_timer.curr_comp);
+
+ if (distance > MCT_GT_COUNTER_STEP || !distance) {
+ distance = MCT_GT_COUNTER_STEP;
+ }
+
+ exynos4210_gfrc_set_count(&s->g_timer, distance);
+ exynos4210_gfrc_start(&s->g_timer);
+}
+
+/*
+ * Raise global timer CMP IRQ
+ */
+static void exynos4210_gcomp_raise_irq(void *opaque, uint32_t id)
+{
+ Exynos4210MCTGT *s = opaque;
+
+ /* If CSTAT is pending and IRQ is enabled */
+ if ((s->reg.int_cstat & G_INT_CSTAT_COMP(id)) &&
+ (s->reg.int_enb & G_INT_ENABLE(id))) {
+ DPRINTF("gcmp timer[%d] IRQ\n", id);
+ qemu_irq_raise(s->irq[id]);
+ }
+}
+
+/*
+ * Lower global timer CMP IRQ
+ */
+static void exynos4210_gcomp_lower_irq(void *opaque, uint32_t id)
+{
+ Exynos4210MCTGT *s = opaque;
+ qemu_irq_lower(s->irq[id]);
+}
+
+/*
+ * Global timer FRC event handler.
+ * Each event occurs when internal counter reaches counter + MCT_GT_COUNTER_STEP
+ * Every time we arm global FRC timer to count for MCT_GT_COUNTER_STEP value
+ */
+static void exynos4210_gfrc_event(void *opaque)
+{
+ Exynos4210MCTState *s = (Exynos4210MCTState *)opaque;
+ int i;
+ uint64_t distance;
+
+ DPRINTF("\n");
+
+ s->g_timer.reg.cnt += s->g_timer.count;
+
+ /* Process all comparators */
+ for (i = 0; i < MCT_GT_CMP_NUM; i++) {
+
+ if (s->g_timer.reg.cnt == s->g_timer.reg.comp[i]) {
+ /* reached nearest comparator */
+
+ s->g_timer.reg.int_cstat |= G_INT_CSTAT_COMP(i);
+
+ /* Auto increment */
+ if (s->g_timer.reg.tcon & G_TCON_AUTO_ICREMENT(i)) {
+ s->g_timer.reg.comp[i] += s->g_timer.reg.comp_add_incr[i];
+ }
+
+ /* IRQ */
+ exynos4210_gcomp_raise_irq(&s->g_timer, i);
+ }
+ }
+
+ /* Reload FRC to reach nearest comparator */
+ s->g_timer.curr_comp = exynos4210_gcomp_find(s);
+ distance = exynos4210_gcomp_get_distance(s, s->g_timer.curr_comp);
+ if (distance > MCT_GT_COUNTER_STEP || !distance) {
+ distance = MCT_GT_COUNTER_STEP;
+ }
+ exynos4210_gfrc_set_count(&s->g_timer, distance);
+
+ exynos4210_gfrc_start(&s->g_timer);
+}
+
+/*
+ * Get counter of FRC local timer.
+ */
+static uint64_t exynos4210_lfrc_get_count(Exynos4210MCTLT *s)
+{
+ return ptimer_get_count(s->ptimer_frc);
+}
+
+/*
+ * Set counter of FRC local timer.
+ */
+static void exynos4210_lfrc_update_count(Exynos4210MCTLT *s)
+{
+ if (!s->reg.cnt[L_REG_CNT_FRCCNTB]) {
+ ptimer_set_count(s->ptimer_frc, MCT_LT_COUNTER_STEP);
+ } else {
+ ptimer_set_count(s->ptimer_frc, s->reg.cnt[L_REG_CNT_FRCCNTB]);
+ }
+}
+
+/*
+ * Start local FRC timer
+ */
+static void exynos4210_lfrc_start(Exynos4210MCTLT *s)
+{
+ ptimer_run(s->ptimer_frc, 1);
+}
+
+/*
+ * Stop local FRC timer
+ */
+static void exynos4210_lfrc_stop(Exynos4210MCTLT *s)
+{
+ ptimer_stop(s->ptimer_frc);
+}
+
+/*
+ * Local timer free running counter tick handler
+ */
+static void exynos4210_lfrc_event(void *opaque)
+{
+ Exynos4210MCTLT * s = (Exynos4210MCTLT *)opaque;
+
+ /* local frc expired */
+
+ DPRINTF("\n");
+
+ s->reg.int_cstat |= L_INT_CSTAT_FRCCNT;
+
+ /* update frc counter */
+ exynos4210_lfrc_update_count(s);
+
+ /* raise irq */
+ if (s->reg.int_enb & L_INT_INTENB_FRCEIE) {
+ qemu_irq_raise(s->irq);
+ }
+
+ /* we reached here, this means that timer is enabled */
+ exynos4210_lfrc_start(s);
+}
+
+static uint32_t exynos4210_ltick_int_get_cnto(struct tick_timer *s);
+static uint32_t exynos4210_ltick_cnt_get_cnto(struct tick_timer *s);
+static void exynos4210_ltick_recalc_count(struct tick_timer *s);
+
+/*
+ * Action on enabling local tick int timer
+ */
+static void exynos4210_ltick_int_start(struct tick_timer *s)
+{
+ if (!s->int_run) {
+ s->int_run = 1;
+ }
+}
+
+/*
+ * Action on disabling local tick int timer
+ */
+static void exynos4210_ltick_int_stop(struct tick_timer *s)
+{
+ if (s->int_run) {
+ s->last_icnto = exynos4210_ltick_int_get_cnto(s);
+ s->int_run = 0;
+ }
+}
+
+/*
+ * Get count for INT timer
+ */
+static uint32_t exynos4210_ltick_int_get_cnto(struct tick_timer *s)
+{
+ uint32_t icnto;
+ uint64_t remain;
+ uint64_t count;
+ uint64_t counted;
+ uint64_t cur_progress;
+
+ count = ptimer_get_count(s->ptimer_tick);
+ if (count) {
+ /* timer is still counting, called not from event */
+ counted = s->count - ptimer_get_count(s->ptimer_tick);
+ cur_progress = s->progress + counted;
+ } else {
+ /* timer expired earlier */
+ cur_progress = s->progress;
+ }
+
+ remain = s->distance - cur_progress;
+
+ if (!s->int_run) {
+ /* INT is stopped. */
+ icnto = s->last_icnto;
+ } else {
+ /* Both are counting */
+ icnto = remain / s->tcntb;
+ }
+
+ return icnto;
+}
+
+/*
+ * Start local tick cnt timer.
+ */
+static void exynos4210_ltick_cnt_start(struct tick_timer *s)
+{
+ if (!s->cnt_run) {
+
+ exynos4210_ltick_recalc_count(s);
+ ptimer_set_count(s->ptimer_tick, s->count);
+ ptimer_run(s->ptimer_tick, 1);
+
+ s->cnt_run = 1;
+ }
+}
+
+/*
+ * Stop local tick cnt timer.
+ */
+static void exynos4210_ltick_cnt_stop(struct tick_timer *s)
+{
+ if (s->cnt_run) {
+
+ s->last_tcnto = exynos4210_ltick_cnt_get_cnto(s);
+
+ if (s->int_run) {
+ exynos4210_ltick_int_stop(s);
+ }
+
+ ptimer_stop(s->ptimer_tick);
+
+ s->cnt_run = 0;
+ }
+}
+
+/*
+ * Get counter for CNT timer
+ */
+static uint32_t exynos4210_ltick_cnt_get_cnto(struct tick_timer *s)
+{
+ uint32_t tcnto;
+ uint32_t icnto;
+ uint64_t remain;
+ uint64_t counted;
+ uint64_t count;
+ uint64_t cur_progress;
+
+ count = ptimer_get_count(s->ptimer_tick);
+ if (count) {
+ /* timer is still counting, called not from event */
+ counted = s->count - ptimer_get_count(s->ptimer_tick);
+ cur_progress = s->progress + counted;
+ } else {
+ /* timer expired earlier */
+ cur_progress = s->progress;
+ }
+
+ remain = s->distance - cur_progress;
+
+ if (!s->cnt_run) {
+ /* Both are stopped. */
+ tcnto = s->last_tcnto;
+ } else if (!s->int_run) {
+ /* INT counter is stopped, progress is by CNT timer */
+ tcnto = remain % s->tcntb;
+ } else {
+ /* Both are counting */
+ icnto = remain / s->tcntb;
+ if (icnto) {
+ tcnto = remain % (icnto * s->tcntb);
+ } else {
+ tcnto = remain % s->tcntb;
+ }
+ }
+
+ return tcnto;
+}
+
+/*
+ * Set new values of counters for CNT and INT timers
+ */
+static void exynos4210_ltick_set_cntb(struct tick_timer *s, uint32_t new_cnt,
+ uint32_t new_int)
+{
+ uint32_t cnt_stopped = 0;
+ uint32_t int_stopped = 0;
+
+ if (s->cnt_run) {
+ exynos4210_ltick_cnt_stop(s);
+ cnt_stopped = 1;
+ }
+
+ if (s->int_run) {
+ exynos4210_ltick_int_stop(s);
+ int_stopped = 1;
+ }
+
+ s->tcntb = new_cnt + 1;
+ s->icntb = new_int + 1;
+
+ if (cnt_stopped) {
+ exynos4210_ltick_cnt_start(s);
+ }
+ if (int_stopped) {
+ exynos4210_ltick_int_start(s);
+ }
+
+}
+
+/*
+ * Calculate new counter value for tick timer
+ */
+static void exynos4210_ltick_recalc_count(struct tick_timer *s)
+{
+ uint64_t to_count;
+
+ if ((s->cnt_run && s->last_tcnto) || (s->int_run && s->last_icnto)) {
+ /*
+ * one or both timers run and not counted to the end;
+ * distance is not passed, recalculate with last_tcnto * last_icnto
+ */
+
+ if (s->last_tcnto) {
+ to_count = s->last_tcnto * s->last_icnto;
+ } else {
+ to_count = s->last_icnto;
+ }
+ } else {
+ /* distance is passed, recalculate with tcnto * icnto */
+ if (s->icntb) {
+ s->distance = s->tcntb * s->icntb;
+ } else {
+ s->distance = s->tcntb;
+ }
+
+ to_count = s->distance;
+ s->progress = 0;
+ }
+
+ if (to_count > MCT_LT_COUNTER_STEP) {
+ /* count by step */
+ s->count = MCT_LT_COUNTER_STEP;
+ } else {
+ s->count = to_count;
+ }
+}
+
+/*
+ * Initialize tick_timer
+ */
+static void exynos4210_ltick_timer_init(struct tick_timer *s)
+{
+ exynos4210_ltick_int_stop(s);
+ exynos4210_ltick_cnt_stop(s);
+
+ s->count = 0;
+ s->distance = 0;
+ s->progress = 0;
+ s->icntb = 0;
+ s->tcntb = 0;
+}
+
+/*
+ * tick_timer event.
+ * Raises when abstract tick_timer expires.
+ */
+static void exynos4210_ltick_timer_event(struct tick_timer *s)
+{
+ s->progress += s->count;
+}
+
+/*
+ * Local timer tick counter handler.
+ * Don't use reloaded timers. If timer counter = zero
+ * then handler called but after handler finished no
+ * timer reload occurs.
+ */
+static void exynos4210_ltick_event(void *opaque)
+{
+ Exynos4210MCTLT * s = (Exynos4210MCTLT *)opaque;
+ uint32_t tcnto;
+ uint32_t icnto;
+#ifdef DEBUG_MCT
+ static uint64_t time1[2] = {0};
+ static uint64_t time2[2] = {0};
+#endif
+
+ /* Call tick_timer event handler, it will update its tcntb and icntb. */
+ exynos4210_ltick_timer_event(&s->tick_timer);
+
+ /* get tick_timer cnt */
+ tcnto = exynos4210_ltick_cnt_get_cnto(&s->tick_timer);
+
+ /* get tick_timer int */
+ icnto = exynos4210_ltick_int_get_cnto(&s->tick_timer);
+
+ /* raise IRQ if needed */
+ if (!icnto && s->reg.tcon & L_TCON_INT_START) {
+ /* INT counter enabled and expired */
+
+ s->reg.int_cstat |= L_INT_CSTAT_INTCNT;
+
+ /* raise interrupt if enabled */
+ if (s->reg.int_enb & L_INT_INTENB_ICNTEIE) {
+#ifdef DEBUG_MCT
+ time2[s->id] = qemu_get_clock_ns(vm_clock);
+ DPRINTF("local timer[%d] IRQ: %llx\n", s->id,
+ time2[s->id] - time1[s->id]);
+ time1[s->id] = time2[s->id];
+#endif
+ qemu_irq_raise(s->irq);
+ }
+
+ /* reload ICNTB */
+ if (s->reg.tcon & L_TCON_INTERVAL_MODE) {
+ exynos4210_ltick_set_cntb(&s->tick_timer,
+ s->reg.cnt[L_REG_CNT_TCNTB],
+ s->reg.cnt[L_REG_CNT_ICNTB]);
+ }
+ } else {
+ /* reload TCNTB */
+ if (!tcnto) {
+ exynos4210_ltick_set_cntb(&s->tick_timer,
+ s->reg.cnt[L_REG_CNT_TCNTB],
+ icnto);
+ }
+ }
+
+ /* start tick_timer cnt */
+ exynos4210_ltick_cnt_start(&s->tick_timer);
+
+ /* start tick_timer int */
+ exynos4210_ltick_int_start(&s->tick_timer);
+}
+
+/* update timer frequency */
+static void exynos4210_mct_update_freq(Exynos4210MCTState *s)
+{
+ uint32_t freq = s->freq;
+ s->freq = 24000000 /
+ ((MCT_CFG_GET_PRESCALER(s->reg_mct_cfg)+1) *
+ MCT_CFG_GET_DIVIDER(s->reg_mct_cfg));
+
+ if (freq != s->freq) {
+ DPRINTF("freq=%dHz\n", s->freq);
+
+ /* global timer */
+ ptimer_set_freq(s->g_timer.ptimer_frc, s->freq);
+
+ /* local timer */
+ ptimer_set_freq(s->l_timer[0].tick_timer.ptimer_tick, s->freq);
+ ptimer_set_freq(s->l_timer[0].ptimer_frc, s->freq);
+ ptimer_set_freq(s->l_timer[1].tick_timer.ptimer_tick, s->freq);
+ ptimer_set_freq(s->l_timer[1].ptimer_frc, s->freq);
+ }
+}
+
+/* set defaul_timer values for all fields */
+static void exynos4210_mct_reset(DeviceState *d)
+{
+ Exynos4210MCTState *s = (Exynos4210MCTState *)d;
+ uint32_t i;
+
+ s->reg_mct_cfg = 0;
+
+ /* global timer */
+ memset(&s->g_timer.reg, 0, sizeof(s->g_timer.reg));
+ exynos4210_gfrc_stop(&s->g_timer);
+
+ /* local timer */
+ memset(s->l_timer[0].reg.cnt, 0, sizeof(s->l_timer[0].reg.cnt));
+ memset(s->l_timer[1].reg.cnt, 0, sizeof(s->l_timer[1].reg.cnt));
+ for (i = 0; i < 2; i++) {
+ s->l_timer[i].reg.int_cstat = 0;
+ s->l_timer[i].reg.int_enb = 0;
+ s->l_timer[i].reg.tcon = 0;
+ s->l_timer[i].reg.wstat = 0;
+ s->l_timer[i].tick_timer.count = 0;
+ s->l_timer[i].tick_timer.distance = 0;
+ s->l_timer[i].tick_timer.progress = 0;
+ ptimer_stop(s->l_timer[i].ptimer_frc);
+
+ exynos4210_ltick_timer_init(&s->l_timer[i].tick_timer);
+ }
+
+ exynos4210_mct_update_freq(s);
+
+}
+
+/* Multi Core Timer read */
+static uint64_t exynos4210_mct_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ Exynos4210MCTState *s = (Exynos4210MCTState *)opaque;
+ int index;
+ int shift;
+ uint64_t count;
+ uint32_t value;
+ int lt_i;
+
+ switch (offset) {
+
+ case MCT_CFG:
+ value = s->reg_mct_cfg;
+ break;
+
+ case G_CNT_L: case G_CNT_U:
+ shift = 8 * (offset & 0x4);
+ count = exynos4210_gfrc_get_count(&s->g_timer);
+ value = UINT32_MAX & (count >> shift);
+ DPRINTF("read FRC=0x%llx\n", count);
+ break;
+
+ case G_CNT_WSTAT:
+ value = s->g_timer.reg.cnt_wstat;
+ break;
+
+ case G_COMP_L(0): case G_COMP_L(1): case G_COMP_L(2): case G_COMP_L(3):
+ case G_COMP_U(0): case G_COMP_U(1): case G_COMP_U(2): case G_COMP_U(3):
+ index = GET_G_COMP_IDX(offset);
+ shift = 8 * (offset & 0x4);
+ value = UINT32_MAX & (s->g_timer.reg.comp[index] >> shift);
+ break;
+
+ case G_TCON:
+ value = s->g_timer.reg.tcon;
+ break;
+
+ case G_INT_CSTAT:
+ value = s->g_timer.reg.int_cstat;
+ break;
+
+ case G_INT_ENB:
+ value = s->g_timer.reg.int_enb;
+ break;
+ break;
+ case G_WSTAT:
+ value = s->g_timer.reg.wstat;
+ break;
+
+ case G_COMP0_ADD_INCR: case G_COMP1_ADD_INCR:
+ case G_COMP2_ADD_INCR: case G_COMP3_ADD_INCR:
+ value = s->g_timer.reg.comp_add_incr[GET_G_COMP_ADD_INCR_IDX(offset)];
+ break;
+
+ /* Local timers */
+ case L0_TCNTB: case L0_ICNTB: case L0_FRCNTB:
+ case L1_TCNTB: case L1_ICNTB: case L1_FRCNTB:
+ lt_i = GET_L_TIMER_IDX(offset);
+ index = GET_L_TIMER_CNT_REG_IDX(offset, lt_i);
+ value = s->l_timer[lt_i].reg.cnt[index];
+ break;
+
+ case L0_TCNTO: case L1_TCNTO:
+ lt_i = GET_L_TIMER_IDX(offset);
+
+ value = exynos4210_ltick_cnt_get_cnto(&s->l_timer[lt_i].tick_timer);
+ DPRINTF("local timer[%d] read TCNTO %x\n", lt_i, value);
+ break;
+
+ case L0_ICNTO: case L1_ICNTO:
+ lt_i = GET_L_TIMER_IDX(offset);
+
+ value = exynos4210_ltick_int_get_cnto(&s->l_timer[lt_i].tick_timer);
+ DPRINTF("local timer[%d] read ICNTO %x\n", lt_i, value);
+ break;
+
+ case L0_FRCNTO: case L1_FRCNTO:
+ lt_i = GET_L_TIMER_IDX(offset);
+
+ value = exynos4210_lfrc_get_count(&s->l_timer[lt_i]);
+
+ break;
+
+ case L0_TCON: case L1_TCON:
+ lt_i = ((offset & 0xF00) - L0_TCNTB) / 0x100;
+ value = s->l_timer[lt_i].reg.tcon;
+ break;
+
+ case L0_INT_CSTAT: case L1_INT_CSTAT:
+ lt_i = ((offset & 0xF00) - L0_TCNTB) / 0x100;
+ value = s->l_timer[lt_i].reg.int_cstat;
+ break;
+
+ case L0_INT_ENB: case L1_INT_ENB:
+ lt_i = ((offset & 0xF00) - L0_TCNTB) / 0x100;
+ value = s->l_timer[lt_i].reg.int_enb;
+ break;
+
+ case L0_WSTAT: case L1_WSTAT:
+ lt_i = ((offset & 0xF00) - L0_TCNTB) / 0x100;
+ value = s->l_timer[lt_i].reg.wstat;
+ break;
+
+ default:
+ hw_error("exynos4210.mct: bad read offset "
+ TARGET_FMT_plx "\n", offset);
+ break;
+ }
+ return value;
+}
+
+/* MCT write */
+static void exynos4210_mct_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ Exynos4210MCTState *s = (Exynos4210MCTState *)opaque;
+ int index; /* index in buffer which represents register set */
+ int shift;
+ int lt_i;
+ uint64_t new_frc;
+ uint32_t i;
+ uint32_t old_val;
+#ifdef DEBUG_MCT
+ static uint32_t icntb_max[2] = {0};
+ static uint32_t icntb_min[2] = {UINT32_MAX, UINT32_MAX};
+ static uint32_t tcntb_max[2] = {0};
+ static uint32_t tcntb_min[2] = {UINT32_MAX, UINT32_MAX};
+#endif
+
+ new_frc = s->g_timer.reg.cnt;
+
+ switch (offset) {
+
+ case MCT_CFG:
+ s->reg_mct_cfg = value;
+ exynos4210_mct_update_freq(s);
+ break;
+
+ case G_CNT_L:
+ case G_CNT_U:
+ if (offset == G_CNT_L) {
+
+ DPRINTF("global timer write to reg.cntl %llx\n", value);
+
+ new_frc = (s->g_timer.reg.cnt & (uint64_t)UINT32_MAX << 32) + value;
+ s->g_timer.reg.cnt_wstat |= G_CNT_WSTAT_L;
+ }
+ if (offset == G_CNT_U) {
+
+ DPRINTF("global timer write to reg.cntu %llx\n", value);
+
+ new_frc = (s->g_timer.reg.cnt & UINT32_MAX) +
+ ((uint64_t)value << 32);
+ s->g_timer.reg.cnt_wstat |= G_CNT_WSTAT_U;
+ }
+
+ s->g_timer.reg.cnt = new_frc;
+ exynos4210_gfrc_restart(s);
+ break;
+
+ case G_CNT_WSTAT:
+ s->g_timer.reg.cnt_wstat &= ~(value);
+ break;
+
+ case G_COMP_L(0): case G_COMP_L(1): case G_COMP_L(2): case G_COMP_L(3):
+ case G_COMP_U(0): case G_COMP_U(1): case G_COMP_U(2): case G_COMP_U(3):
+ index = GET_G_COMP_IDX(offset);
+ shift = 8 * (offset & 0x4);
+ s->g_timer.reg.comp[index] =
+ (s->g_timer.reg.comp[index] &
+ (((uint64_t)UINT32_MAX << 32) >> shift)) +
+ (value << shift);
+
+ DPRINTF("comparator %d write 0x%llx val << %d\n", index, value, shift);
+
+ if (offset&0x4) {
+ s->g_timer.reg.wstat |= G_WSTAT_COMP_U(index);
+ } else {
+ s->g_timer.reg.wstat |= G_WSTAT_COMP_L(index);
+ }
+
+ exynos4210_gfrc_restart(s);
+ break;
+
+ case G_TCON:
+ old_val = s->g_timer.reg.tcon;
+ s->g_timer.reg.tcon = value;
+ s->g_timer.reg.wstat |= G_WSTAT_TCON_WRITE;
+
+ DPRINTF("global timer write to reg.g_tcon %llx\n", value);
+
+ /* Start FRC if transition from disabled to enabled */
+ if ((value & G_TCON_TIMER_ENABLE) > (old_val &
+ G_TCON_TIMER_ENABLE)) {
+ exynos4210_gfrc_start(&s->g_timer);
+ }
+ if ((value & G_TCON_TIMER_ENABLE) < (old_val &
+ G_TCON_TIMER_ENABLE)) {
+ exynos4210_gfrc_stop(&s->g_timer);
+ }
+
+ /* Start CMP if transition from disabled to enabled */
+ for (i = 0; i < MCT_GT_CMP_NUM; i++) {
+ if ((value & G_TCON_COMP_ENABLE(i)) != (old_val &
+ G_TCON_COMP_ENABLE(i))) {
+ exynos4210_gfrc_restart(s);
+ }
+ }
+ break;
+
+ case G_INT_CSTAT:
+ s->g_timer.reg.int_cstat &= ~(value);
+ for (i = 0; i < MCT_GT_CMP_NUM; i++) {
+ if (value & G_INT_CSTAT_COMP(i)) {
+ exynos4210_gcomp_lower_irq(&s->g_timer, i);
+ }
+ }
+ break;
+
+ case G_INT_ENB:
+
+ /* Raise IRQ if transition from disabled to enabled and CSTAT pending */
+ for (i = 0; i < MCT_GT_CMP_NUM; i++) {
+ if ((value & G_INT_ENABLE(i)) > (s->g_timer.reg.tcon &
+ G_INT_ENABLE(i))) {
+ if (s->g_timer.reg.int_cstat & G_INT_CSTAT_COMP(i)) {
+ exynos4210_gcomp_raise_irq(&s->g_timer, i);
+ }
+ }
+
+ if ((value & G_INT_ENABLE(i)) < (s->g_timer.reg.tcon &
+ G_INT_ENABLE(i))) {
+ exynos4210_gcomp_lower_irq(&s->g_timer, i);
+ }
+ }
+
+ DPRINTF("global timer INT enable %llx\n", value);
+ s->g_timer.reg.int_enb = value;
+ break;
+
+ case G_WSTAT:
+ s->g_timer.reg.wstat &= ~(value);
+ break;
+
+ case G_COMP0_ADD_INCR: case G_COMP1_ADD_INCR:
+ case G_COMP2_ADD_INCR: case G_COMP3_ADD_INCR:
+ index = GET_G_COMP_ADD_INCR_IDX(offset);
+ s->g_timer.reg.comp_add_incr[index] = value;
+ s->g_timer.reg.wstat |= G_WSTAT_COMP_ADDINCR(index);
+ break;
+
+ /* Local timers */
+ case L0_TCON: case L1_TCON:
+ lt_i = GET_L_TIMER_IDX(offset);
+ old_val = s->l_timer[lt_i].reg.tcon;
+
+ s->l_timer[lt_i].reg.wstat |= L_WSTAT_TCON_WRITE;
+ s->l_timer[lt_i].reg.tcon = value;
+
+ /* Stop local CNT */
+ if ((value & L_TCON_TICK_START) <
+ (old_val & L_TCON_TICK_START)) {
+ DPRINTF("local timer[%d] stop cnt\n", lt_i);
+ exynos4210_ltick_cnt_stop(&s->l_timer[lt_i].tick_timer);
+ }
+
+ /* Stop local INT */
+ if ((value & L_TCON_INT_START) <
+ (old_val & L_TCON_INT_START)) {
+ DPRINTF("local timer[%d] stop int\n", lt_i);
+ exynos4210_ltick_int_stop(&s->l_timer[lt_i].tick_timer);
+ }
+
+ /* Start local CNT */
+ if ((value & L_TCON_TICK_START) >
+ (old_val & L_TCON_TICK_START)) {
+ DPRINTF("local timer[%d] start cnt\n", lt_i);
+ exynos4210_ltick_cnt_start(&s->l_timer[lt_i].tick_timer);
+ }
+
+ /* Start local INT */
+ if ((value & L_TCON_INT_START) >
+ (old_val & L_TCON_INT_START)) {
+ DPRINTF("local timer[%d] start int\n", lt_i);
+ exynos4210_ltick_int_start(&s->l_timer[lt_i].tick_timer);
+ }
+
+ /* Start or Stop local FRC if TCON changed */
+ if ((value & L_TCON_FRC_START) >
+ (s->l_timer[lt_i].reg.tcon & L_TCON_FRC_START)) {
+ DPRINTF("local timer[%d] start frc\n", lt_i);
+ exynos4210_lfrc_start(&s->l_timer[lt_i]);
+ }
+ if ((value & L_TCON_FRC_START) <
+ (s->l_timer[lt_i].reg.tcon & L_TCON_FRC_START)) {
+ DPRINTF("local timer[%d] stop frc\n", lt_i);
+ exynos4210_lfrc_stop(&s->l_timer[lt_i]);
+ }
+ break;
+
+ case L0_TCNTB: case L1_TCNTB:
+
+ lt_i = GET_L_TIMER_IDX(offset);
+ index = GET_L_TIMER_CNT_REG_IDX(offset, lt_i);
+
+ /*
+ * TCNTB is updated to internal register only after CNT expired.
+ * Due to this we should reload timer to nearest moment when CNT is
+ * expired and then in event handler update tcntb to new TCNTB value.
+ */
+ exynos4210_ltick_set_cntb(&s->l_timer[lt_i].tick_timer, value,
+ s->l_timer[lt_i].tick_timer.icntb);
+
+ s->l_timer[lt_i].reg.wstat |= L_WSTAT_TCNTB_WRITE;
+ s->l_timer[lt_i].reg.cnt[L_REG_CNT_TCNTB] = value;
+
+#ifdef DEBUG_MCT
+ if (tcntb_min[lt_i] > value) {
+ tcntb_min[lt_i] = value;
+ }
+ if (tcntb_max[lt_i] < value) {
+ tcntb_max[lt_i] = value;
+ }
+ DPRINTF("local timer[%d] TCNTB write %llx; max=%x, min=%x\n",
+ lt_i, value, tcntb_max[lt_i], tcntb_min[lt_i]);
+#endif
+ break;
+
+ case L0_ICNTB: case L1_ICNTB:
+
+ lt_i = GET_L_TIMER_IDX(offset);
+ index = GET_L_TIMER_CNT_REG_IDX(offset, lt_i);
+
+ s->l_timer[lt_i].reg.wstat |= L_WSTAT_ICNTB_WRITE;
+ s->l_timer[lt_i].reg.cnt[L_REG_CNT_ICNTB] = value &
+ ~L_ICNTB_MANUAL_UPDATE;
+
+ /*
+ * We need to avoid too small values for TCNTB*ICNTB. If not, IRQ event
+ * could raise too fast disallowing QEMU to execute target code.
+ */
+ if (s->l_timer[lt_i].reg.cnt[L_REG_CNT_ICNTB] *
+ s->l_timer[lt_i].reg.cnt[L_REG_CNT_TCNTB] < MCT_LT_CNT_LOW_LIMIT) {
+ if (!s->l_timer[lt_i].reg.cnt[L_REG_CNT_TCNTB]) {
+ s->l_timer[lt_i].reg.cnt[L_REG_CNT_ICNTB] =
+ MCT_LT_CNT_LOW_LIMIT;
+ } else {
+ s->l_timer[lt_i].reg.cnt[L_REG_CNT_ICNTB] =
+ MCT_LT_CNT_LOW_LIMIT /
+ s->l_timer[lt_i].reg.cnt[L_REG_CNT_TCNTB];
+ }
+ }
+
+ if (value & L_ICNTB_MANUAL_UPDATE) {
+ exynos4210_ltick_set_cntb(&s->l_timer[lt_i].tick_timer,
+ s->l_timer[lt_i].tick_timer.tcntb,
+ s->l_timer[lt_i].reg.cnt[L_REG_CNT_ICNTB]);
+ }
+
+#ifdef DEBUG_MCT
+ if (icntb_min[lt_i] > value) {
+ icntb_min[lt_i] = value;
+ }
+ if (icntb_max[lt_i] < value) {
+ icntb_max[lt_i] = value;
+ }
+DPRINTF("local timer[%d] ICNTB write %llx; max=%x, min=%x\n\n",
+ lt_i, value, icntb_max[lt_i], icntb_min[lt_i]);
+#endif
+break;
+
+ case L0_FRCNTB: case L1_FRCNTB:
+
+ lt_i = GET_L_TIMER_IDX(offset);
+ index = GET_L_TIMER_CNT_REG_IDX(offset, lt_i);
+
+ DPRINTF("local timer[%d] FRCNTB write %llx\n", lt_i, value);
+
+ s->l_timer[lt_i].reg.wstat |= L_WSTAT_FRCCNTB_WRITE;
+ s->l_timer[lt_i].reg.cnt[L_REG_CNT_FRCCNTB] = value;
+
+ break;
+
+ case L0_TCNTO: case L1_TCNTO:
+ case L0_ICNTO: case L1_ICNTO:
+ case L0_FRCNTO: case L1_FRCNTO:
+ fprintf(stderr, "\n[exynos4210.mct: write to RO register "
+ TARGET_FMT_plx "]\n\n", offset);
+ break;
+
+ case L0_INT_CSTAT: case L1_INT_CSTAT:
+ lt_i = GET_L_TIMER_IDX(offset);
+
+ DPRINTF("local timer[%d] CSTAT write %llx\n", lt_i, value);
+
+ s->l_timer[lt_i].reg.int_cstat &= ~value;
+ if (!s->l_timer[lt_i].reg.int_cstat) {
+ qemu_irq_lower(s->l_timer[lt_i].irq);
+ }
+ break;
+
+ case L0_INT_ENB: case L1_INT_ENB:
+ lt_i = GET_L_TIMER_IDX(offset);
+ old_val = s->l_timer[lt_i].reg.int_enb;
+
+ /* Raise Local timer IRQ if cstat is pending */
+ if ((value & L_INT_INTENB_ICNTEIE) > (old_val & L_INT_INTENB_ICNTEIE)) {
+ if (s->l_timer[lt_i].reg.int_cstat & L_INT_CSTAT_INTCNT) {
+ qemu_irq_raise(s->l_timer[lt_i].irq);
+ }
+ }
+
+ s->l_timer[lt_i].reg.int_enb = value;
+
+ break;
+
+ case L0_WSTAT: case L1_WSTAT:
+ lt_i = GET_L_TIMER_IDX(offset);
+
+ s->l_timer[lt_i].reg.wstat &= ~value;
+ break;
+
+ default:
+ hw_error("exynos4210.mct: bad write offset "
+ TARGET_FMT_plx "\n", offset);
+ break;
+ }
+}
+
+static const MemoryRegionOps exynos4210_mct_ops = {
+ .read = exynos4210_mct_read,
+ .write = exynos4210_mct_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+/* MCT init */
+static int exynos4210_mct_init(SysBusDevice *dev)
+{
+ int i;
+ Exynos4210MCTState *s = FROM_SYSBUS(Exynos4210MCTState, dev);
+ QEMUBH *bh[2];
+
+ /* Global timer */
+ bh[0] = qemu_bh_new(exynos4210_gfrc_event, s);
+ s->g_timer.ptimer_frc = ptimer_init(bh[0]);
+ memset(&s->g_timer.reg, 0, sizeof(struct gregs));
+
+ /* Local timers */
+ for (i = 0; i < 2; i++) {
+ bh[0] = qemu_bh_new(exynos4210_ltick_event, &s->l_timer[i]);
+ bh[1] = qemu_bh_new(exynos4210_lfrc_event, &s->l_timer[i]);
+ s->l_timer[i].tick_timer.ptimer_tick = ptimer_init(bh[0]);
+ s->l_timer[i].ptimer_frc = ptimer_init(bh[1]);
+ s->l_timer[i].id = i;
+ }
+
+ /* IRQs */
+ for (i = 0; i < MCT_GT_CMP_NUM; i++) {
+ sysbus_init_irq(dev, &s->g_timer.irq[i]);
+ }
+ for (i = 0; i < 2; i++) {
+ sysbus_init_irq(dev, &s->l_timer[i].irq);
+ }
+
+ memory_region_init_io(&s->iomem, &exynos4210_mct_ops, s, "exynos4210-mct",
+ MCT_SFR_SIZE);
+ sysbus_init_mmio(dev, &s->iomem);
+
+ return 0;
+}
+
+static void exynos4210_mct_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = exynos4210_mct_init;
+ dc->reset = exynos4210_mct_reset;
+ dc->vmsd = &vmstate_exynos4210_mct_state;
+}
+
+static const TypeInfo exynos4210_mct_info = {
+ .name = "exynos4210.mct",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(Exynos4210MCTState),
+ .class_init = exynos4210_mct_class_init,
+};
+
+static void exynos4210_mct_register_types(void)
+{
+ type_register_static(&exynos4210_mct_info);
+}
+
+type_init(exynos4210_mct_register_types)
--- /dev/null
+/*
+ * Samsung exynos4210 Pulse Width Modulation Timer
+ *
+ * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd.
+ * All rights reserved.
+ *
+ * Evgeny Voevodin <e.voevodin@samsung.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 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "hw/sysbus.h"
+#include "qemu/timer.h"
+#include "qemu-common.h"
+#include "hw/ptimer.h"
+
+#include "hw/arm/exynos4210.h"
+
+//#define DEBUG_PWM
+
+#ifdef DEBUG_PWM
+#define DPRINTF(fmt, ...) \
+ do { fprintf(stdout, "PWM: [%24s:%5d] " fmt, __func__, __LINE__, \
+ ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while (0)
+#endif
+
+#define EXYNOS4210_PWM_TIMERS_NUM 5
+#define EXYNOS4210_PWM_REG_MEM_SIZE 0x50
+
+#define TCFG0 0x0000
+#define TCFG1 0x0004
+#define TCON 0x0008
+#define TCNTB0 0x000C
+#define TCMPB0 0x0010
+#define TCNTO0 0x0014
+#define TCNTB1 0x0018
+#define TCMPB1 0x001C
+#define TCNTO1 0x0020
+#define TCNTB2 0x0024
+#define TCMPB2 0x0028
+#define TCNTO2 0x002C
+#define TCNTB3 0x0030
+#define TCMPB3 0x0034
+#define TCNTO3 0x0038
+#define TCNTB4 0x003C
+#define TCNTO4 0x0040
+#define TINT_CSTAT 0x0044
+
+#define TCNTB(x) (0xC * (x))
+#define TCMPB(x) (0xC * (x) + 1)
+#define TCNTO(x) (0xC * (x) + 2)
+
+#define GET_PRESCALER(reg, x) (((reg) & (0xFF << (8 * (x)))) >> 8 * (x))
+#define GET_DIVIDER(reg, x) (1 << (((reg) & (0xF << (4 * (x)))) >> (4 * (x))))
+
+/*
+ * Attention! Timer4 doesn't have OUTPUT_INVERTER,
+ * so Auto Reload bit is not accessible by macros!
+ */
+#define TCON_TIMER_BASE(x) (((x) ? 1 : 0) * 4 + 4 * (x))
+#define TCON_TIMER_START(x) (1 << (TCON_TIMER_BASE(x) + 0))
+#define TCON_TIMER_MANUAL_UPD(x) (1 << (TCON_TIMER_BASE(x) + 1))
+#define TCON_TIMER_OUTPUT_INV(x) (1 << (TCON_TIMER_BASE(x) + 2))
+#define TCON_TIMER_AUTO_RELOAD(x) (1 << (TCON_TIMER_BASE(x) + 3))
+#define TCON_TIMER4_AUTO_RELOAD (1 << 22)
+
+#define TINT_CSTAT_STATUS(x) (1 << (5 + (x)))
+#define TINT_CSTAT_ENABLE(x) (1 << (x))
+
+/* timer struct */
+typedef struct {
+ uint32_t id; /* timer id */
+ qemu_irq irq; /* local timer irq */
+ uint32_t freq; /* timer frequency */
+
+ /* use ptimer.c to represent count down timer */
+ ptimer_state *ptimer; /* timer */
+
+ /* registers */
+ uint32_t reg_tcntb; /* counter register buffer */
+ uint32_t reg_tcmpb; /* compare register buffer */
+
+ struct Exynos4210PWMState *parent;
+
+} Exynos4210PWM;
+
+
+typedef struct Exynos4210PWMState {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+
+ uint32_t reg_tcfg[2];
+ uint32_t reg_tcon;
+ uint32_t reg_tint_cstat;
+
+ Exynos4210PWM timer[EXYNOS4210_PWM_TIMERS_NUM];
+
+} Exynos4210PWMState;
+
+/*** VMState ***/
+static const VMStateDescription vmstate_exynos4210_pwm = {
+ .name = "exynos4210.pwm.pwm",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(id, Exynos4210PWM),
+ VMSTATE_UINT32(freq, Exynos4210PWM),
+ VMSTATE_PTIMER(ptimer, Exynos4210PWM),
+ VMSTATE_UINT32(reg_tcntb, Exynos4210PWM),
+ VMSTATE_UINT32(reg_tcmpb, Exynos4210PWM),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_exynos4210_pwm_state = {
+ .name = "exynos4210.pwm",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(reg_tcfg, Exynos4210PWMState, 2),
+ VMSTATE_UINT32(reg_tcon, Exynos4210PWMState),
+ VMSTATE_UINT32(reg_tint_cstat, Exynos4210PWMState),
+ VMSTATE_STRUCT_ARRAY(timer, Exynos4210PWMState,
+ EXYNOS4210_PWM_TIMERS_NUM, 0,
+ vmstate_exynos4210_pwm, Exynos4210PWM),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+/*
+ * PWM update frequency
+ */
+static void exynos4210_pwm_update_freq(Exynos4210PWMState *s, uint32_t id)
+{
+ uint32_t freq;
+ freq = s->timer[id].freq;
+ if (id > 1) {
+ s->timer[id].freq = 24000000 /
+ ((GET_PRESCALER(s->reg_tcfg[0], 1) + 1) *
+ (GET_DIVIDER(s->reg_tcfg[1], id)));
+ } else {
+ s->timer[id].freq = 24000000 /
+ ((GET_PRESCALER(s->reg_tcfg[0], 0) + 1) *
+ (GET_DIVIDER(s->reg_tcfg[1], id)));
+ }
+
+ if (freq != s->timer[id].freq) {
+ ptimer_set_freq(s->timer[id].ptimer, s->timer[id].freq);
+ DPRINTF("freq=%dHz\n", s->timer[id].freq);
+ }
+}
+
+/*
+ * Counter tick handler
+ */
+static void exynos4210_pwm_tick(void *opaque)
+{
+ Exynos4210PWM *s = (Exynos4210PWM *)opaque;
+ Exynos4210PWMState *p = (Exynos4210PWMState *)s->parent;
+ uint32_t id = s->id;
+ bool cmp;
+
+ DPRINTF("timer %d tick\n", id);
+
+ /* set irq status */
+ p->reg_tint_cstat |= TINT_CSTAT_STATUS(id);
+
+ /* raise IRQ */
+ if (p->reg_tint_cstat & TINT_CSTAT_ENABLE(id)) {
+ DPRINTF("timer %d IRQ\n", id);
+ qemu_irq_raise(p->timer[id].irq);
+ }
+
+ /* reload timer */
+ if (id != 4) {
+ cmp = p->reg_tcon & TCON_TIMER_AUTO_RELOAD(id);
+ } else {
+ cmp = p->reg_tcon & TCON_TIMER4_AUTO_RELOAD;
+ }
+
+ if (cmp) {
+ DPRINTF("auto reload timer %d count to %x\n", id,
+ p->timer[id].reg_tcntb);
+ ptimer_set_count(p->timer[id].ptimer, p->timer[id].reg_tcntb);
+ ptimer_run(p->timer[id].ptimer, 1);
+ } else {
+ /* stop timer, set status to STOP, see Basic Timer Operation */
+ p->reg_tcon &= ~TCON_TIMER_START(id);
+ ptimer_stop(p->timer[id].ptimer);
+ }
+}
+
+/*
+ * PWM Read
+ */
+static uint64_t exynos4210_pwm_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ Exynos4210PWMState *s = (Exynos4210PWMState *)opaque;
+ uint32_t value = 0;
+ int index;
+
+ switch (offset) {
+ case TCFG0: case TCFG1:
+ index = (offset - TCFG0) >> 2;
+ value = s->reg_tcfg[index];
+ break;
+
+ case TCON:
+ value = s->reg_tcon;
+ break;
+
+ case TCNTB0: case TCNTB1:
+ case TCNTB2: case TCNTB3: case TCNTB4:
+ index = (offset - TCNTB0) / 0xC;
+ value = s->timer[index].reg_tcntb;
+ break;
+
+ case TCMPB0: case TCMPB1:
+ case TCMPB2: case TCMPB3:
+ index = (offset - TCMPB0) / 0xC;
+ value = s->timer[index].reg_tcmpb;
+ break;
+
+ case TCNTO0: case TCNTO1:
+ case TCNTO2: case TCNTO3: case TCNTO4:
+ index = (offset == TCNTO4) ? 4 : (offset - TCNTO0) / 0xC;
+ value = ptimer_get_count(s->timer[index].ptimer);
+ break;
+
+ case TINT_CSTAT:
+ value = s->reg_tint_cstat;
+ break;
+
+ default:
+ fprintf(stderr,
+ "[exynos4210.pwm: bad read offset " TARGET_FMT_plx "]\n",
+ offset);
+ break;
+ }
+ return value;
+}
+
+/*
+ * PWM Write
+ */
+static void exynos4210_pwm_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ Exynos4210PWMState *s = (Exynos4210PWMState *)opaque;
+ int index;
+ uint32_t new_val;
+ int i;
+
+ switch (offset) {
+ case TCFG0: case TCFG1:
+ index = (offset - TCFG0) >> 2;
+ s->reg_tcfg[index] = value;
+
+ /* update timers frequencies */
+ for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) {
+ exynos4210_pwm_update_freq(s, s->timer[i].id);
+ }
+ break;
+
+ case TCON:
+ for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) {
+ if ((value & TCON_TIMER_MANUAL_UPD(i)) >
+ (s->reg_tcon & TCON_TIMER_MANUAL_UPD(i))) {
+ /*
+ * TCNTB and TCMPB are loaded into TCNT and TCMP.
+ * Update timers.
+ */
+
+ /* this will start timer to run, this ok, because
+ * during processing start bit timer will be stopped
+ * if needed */
+ ptimer_set_count(s->timer[i].ptimer, s->timer[i].reg_tcntb);
+ DPRINTF("set timer %d count to %x\n", i,
+ s->timer[i].reg_tcntb);
+ }
+
+ if ((value & TCON_TIMER_START(i)) >
+ (s->reg_tcon & TCON_TIMER_START(i))) {
+ /* changed to start */
+ ptimer_run(s->timer[i].ptimer, 1);
+ DPRINTF("run timer %d\n", i);
+ }
+
+ if ((value & TCON_TIMER_START(i)) <
+ (s->reg_tcon & TCON_TIMER_START(i))) {
+ /* changed to stop */
+ ptimer_stop(s->timer[i].ptimer);
+ DPRINTF("stop timer %d\n", i);
+ }
+ }
+ s->reg_tcon = value;
+ break;
+
+ case TCNTB0: case TCNTB1:
+ case TCNTB2: case TCNTB3: case TCNTB4:
+ index = (offset - TCNTB0) / 0xC;
+ s->timer[index].reg_tcntb = value;
+ break;
+
+ case TCMPB0: case TCMPB1:
+ case TCMPB2: case TCMPB3:
+ index = (offset - TCMPB0) / 0xC;
+ s->timer[index].reg_tcmpb = value;
+ break;
+
+ case TINT_CSTAT:
+ new_val = (s->reg_tint_cstat & 0x3E0) + (0x1F & value);
+ new_val &= ~(0x3E0 & value);
+
+ for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) {
+ if ((new_val & TINT_CSTAT_STATUS(i)) <
+ (s->reg_tint_cstat & TINT_CSTAT_STATUS(i))) {
+ qemu_irq_lower(s->timer[i].irq);
+ }
+ }
+
+ s->reg_tint_cstat = new_val;
+ break;
+
+ default:
+ fprintf(stderr,
+ "[exynos4210.pwm: bad write offset " TARGET_FMT_plx "]\n",
+ offset);
+ break;
+
+ }
+}
+
+/*
+ * Set default values to timer fields and registers
+ */
+static void exynos4210_pwm_reset(DeviceState *d)
+{
+ Exynos4210PWMState *s = (Exynos4210PWMState *)d;
+ int i;
+ s->reg_tcfg[0] = 0x0101;
+ s->reg_tcfg[1] = 0x0;
+ s->reg_tcon = 0;
+ s->reg_tint_cstat = 0;
+ for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) {
+ s->timer[i].reg_tcmpb = 0;
+ s->timer[i].reg_tcntb = 0;
+
+ exynos4210_pwm_update_freq(s, s->timer[i].id);
+ ptimer_stop(s->timer[i].ptimer);
+ }
+}
+
+static const MemoryRegionOps exynos4210_pwm_ops = {
+ .read = exynos4210_pwm_read,
+ .write = exynos4210_pwm_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+/*
+ * PWM timer initialization
+ */
+static int exynos4210_pwm_init(SysBusDevice *dev)
+{
+ Exynos4210PWMState *s = FROM_SYSBUS(Exynos4210PWMState, dev);
+ int i;
+ QEMUBH *bh;
+
+ for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) {
+ bh = qemu_bh_new(exynos4210_pwm_tick, &s->timer[i]);
+ sysbus_init_irq(dev, &s->timer[i].irq);
+ s->timer[i].ptimer = ptimer_init(bh);
+ s->timer[i].id = i;
+ s->timer[i].parent = s;
+ }
+
+ memory_region_init_io(&s->iomem, &exynos4210_pwm_ops, s, "exynos4210-pwm",
+ EXYNOS4210_PWM_REG_MEM_SIZE);
+ sysbus_init_mmio(dev, &s->iomem);
+
+ return 0;
+}
+
+static void exynos4210_pwm_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = exynos4210_pwm_init;
+ dc->reset = exynos4210_pwm_reset;
+ dc->vmsd = &vmstate_exynos4210_pwm_state;
+}
+
+static const TypeInfo exynos4210_pwm_info = {
+ .name = "exynos4210.pwm",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(Exynos4210PWMState),
+ .class_init = exynos4210_pwm_class_init,
+};
+
+static void exynos4210_pwm_register_types(void)
+{
+ type_register_static(&exynos4210_pwm_info);
+}
+
+type_init(exynos4210_pwm_register_types)
--- /dev/null
+/*
+ * Samsung exynos4210 Real Time Clock
+ *
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ * Ogurtsov Oleg <o.ogurtsov@samsung.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 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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+/* Description:
+ * Register RTCCON:
+ * CLKSEL Bit[1] not used
+ * CLKOUTEN Bit[9] not used
+ */
+
+#include "hw/sysbus.h"
+#include "qemu/timer.h"
+#include "qemu-common.h"
+#include "hw/ptimer.h"
+
+#include "hw/hw.h"
+#include "qemu/timer.h"
+#include "sysemu/sysemu.h"
+
+#include "hw/arm/exynos4210.h"
+
+#define DEBUG_RTC 0
+
+#if DEBUG_RTC
+#define DPRINTF(fmt, ...) \
+ do { fprintf(stdout, "RTC: [%24s:%5d] " fmt, __func__, __LINE__, \
+ ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while (0)
+#endif
+
+#define EXYNOS4210_RTC_REG_MEM_SIZE 0x0100
+
+#define INTP 0x0030
+#define RTCCON 0x0040
+#define TICCNT 0x0044
+#define RTCALM 0x0050
+#define ALMSEC 0x0054
+#define ALMMIN 0x0058
+#define ALMHOUR 0x005C
+#define ALMDAY 0x0060
+#define ALMMON 0x0064
+#define ALMYEAR 0x0068
+#define BCDSEC 0x0070
+#define BCDMIN 0x0074
+#define BCDHOUR 0x0078
+#define BCDDAY 0x007C
+#define BCDDAYWEEK 0x0080
+#define BCDMON 0x0084
+#define BCDYEAR 0x0088
+#define CURTICNT 0x0090
+
+#define TICK_TIMER_ENABLE 0x0100
+#define TICNT_THRESHHOLD 2
+
+
+#define RTC_ENABLE 0x0001
+
+#define INTP_TICK_ENABLE 0x0001
+#define INTP_ALM_ENABLE 0x0002
+
+#define ALARM_INT_ENABLE 0x0040
+
+#define RTC_BASE_FREQ 32768
+
+typedef struct Exynos4210RTCState {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+
+ /* registers */
+ uint32_t reg_intp;
+ uint32_t reg_rtccon;
+ uint32_t reg_ticcnt;
+ uint32_t reg_rtcalm;
+ uint32_t reg_almsec;
+ uint32_t reg_almmin;
+ uint32_t reg_almhour;
+ uint32_t reg_almday;
+ uint32_t reg_almmon;
+ uint32_t reg_almyear;
+ uint32_t reg_curticcnt;
+
+ ptimer_state *ptimer; /* tick timer */
+ ptimer_state *ptimer_1Hz; /* clock timer */
+ uint32_t freq;
+
+ qemu_irq tick_irq; /* Time Tick Generator irq */
+ qemu_irq alm_irq; /* alarm irq */
+
+ struct tm current_tm; /* current time */
+} Exynos4210RTCState;
+
+#define TICCKSEL(value) ((value & (0x0F << 4)) >> 4)
+
+/*** VMState ***/
+static const VMStateDescription vmstate_exynos4210_rtc_state = {
+ .name = "exynos4210.rtc",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(reg_intp, Exynos4210RTCState),
+ VMSTATE_UINT32(reg_rtccon, Exynos4210RTCState),
+ VMSTATE_UINT32(reg_ticcnt, Exynos4210RTCState),
+ VMSTATE_UINT32(reg_rtcalm, Exynos4210RTCState),
+ VMSTATE_UINT32(reg_almsec, Exynos4210RTCState),
+ VMSTATE_UINT32(reg_almmin, Exynos4210RTCState),
+ VMSTATE_UINT32(reg_almhour, Exynos4210RTCState),
+ VMSTATE_UINT32(reg_almday, Exynos4210RTCState),
+ VMSTATE_UINT32(reg_almmon, Exynos4210RTCState),
+ VMSTATE_UINT32(reg_almyear, Exynos4210RTCState),
+ VMSTATE_UINT32(reg_curticcnt, Exynos4210RTCState),
+ VMSTATE_PTIMER(ptimer, Exynos4210RTCState),
+ VMSTATE_PTIMER(ptimer_1Hz, Exynos4210RTCState),
+ VMSTATE_UINT32(freq, Exynos4210RTCState),
+ VMSTATE_INT32(current_tm.tm_sec, Exynos4210RTCState),
+ VMSTATE_INT32(current_tm.tm_min, Exynos4210RTCState),
+ VMSTATE_INT32(current_tm.tm_hour, Exynos4210RTCState),
+ VMSTATE_INT32(current_tm.tm_wday, Exynos4210RTCState),
+ VMSTATE_INT32(current_tm.tm_mday, Exynos4210RTCState),
+ VMSTATE_INT32(current_tm.tm_mon, Exynos4210RTCState),
+ VMSTATE_INT32(current_tm.tm_year, Exynos4210RTCState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+#define BCD3DIGITS(x) \
+ ((uint32_t)to_bcd((uint8_t)(x % 100)) + \
+ ((uint32_t)to_bcd((uint8_t)((x % 1000) / 100)) << 8))
+
+static void check_alarm_raise(Exynos4210RTCState *s)
+{
+ unsigned int alarm_raise = 0;
+ struct tm stm = s->current_tm;
+
+ if ((s->reg_rtcalm & 0x01) &&
+ (to_bcd((uint8_t)stm.tm_sec) == (uint8_t)s->reg_almsec)) {
+ alarm_raise = 1;
+ }
+ if ((s->reg_rtcalm & 0x02) &&
+ (to_bcd((uint8_t)stm.tm_min) == (uint8_t)s->reg_almmin)) {
+ alarm_raise = 1;
+ }
+ if ((s->reg_rtcalm & 0x04) &&
+ (to_bcd((uint8_t)stm.tm_hour) == (uint8_t)s->reg_almhour)) {
+ alarm_raise = 1;
+ }
+ if ((s->reg_rtcalm & 0x08) &&
+ (to_bcd((uint8_t)stm.tm_mday) == (uint8_t)s->reg_almday)) {
+ alarm_raise = 1;
+ }
+ if ((s->reg_rtcalm & 0x10) &&
+ (to_bcd((uint8_t)stm.tm_mon) == (uint8_t)s->reg_almmon)) {
+ alarm_raise = 1;
+ }
+ if ((s->reg_rtcalm & 0x20) &&
+ (BCD3DIGITS(stm.tm_year) == s->reg_almyear)) {
+ alarm_raise = 1;
+ }
+
+ if (alarm_raise) {
+ DPRINTF("ALARM IRQ\n");
+ /* set irq status */
+ s->reg_intp |= INTP_ALM_ENABLE;
+ qemu_irq_raise(s->alm_irq);
+ }
+}
+
+/*
+ * RTC update frequency
+ * Parameters:
+ * reg_value - current RTCCON register or his new value
+ */
+static void exynos4210_rtc_update_freq(Exynos4210RTCState *s,
+ uint32_t reg_value)
+{
+ uint32_t freq;
+
+ freq = s->freq;
+ /* set frequncy for time generator */
+ s->freq = RTC_BASE_FREQ / (1 << TICCKSEL(reg_value));
+
+ if (freq != s->freq) {
+ ptimer_set_freq(s->ptimer, s->freq);
+ DPRINTF("freq=%dHz\n", s->freq);
+ }
+}
+
+/* month is between 0 and 11. */
+static int get_days_in_month(int month, int year)
+{
+ static const int days_tab[12] = {
+ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+ };
+ int d;
+ if ((unsigned)month >= 12) {
+ return 31;
+ }
+ d = days_tab[month];
+ if (month == 1) {
+ if ((year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0)) {
+ d++;
+ }
+ }
+ return d;
+}
+
+/* update 'tm' to the next second */
+static void rtc_next_second(struct tm *tm)
+{
+ int days_in_month;
+
+ tm->tm_sec++;
+ if ((unsigned)tm->tm_sec >= 60) {
+ tm->tm_sec = 0;
+ tm->tm_min++;
+ if ((unsigned)tm->tm_min >= 60) {
+ tm->tm_min = 0;
+ tm->tm_hour++;
+ if ((unsigned)tm->tm_hour >= 24) {
+ tm->tm_hour = 0;
+ /* next day */
+ tm->tm_wday++;
+ if ((unsigned)tm->tm_wday >= 7) {
+ tm->tm_wday = 0;
+ }
+ days_in_month = get_days_in_month(tm->tm_mon,
+ tm->tm_year + 1900);
+ tm->tm_mday++;
+ if (tm->tm_mday < 1) {
+ tm->tm_mday = 1;
+ } else if (tm->tm_mday > days_in_month) {
+ tm->tm_mday = 1;
+ tm->tm_mon++;
+ if (tm->tm_mon >= 12) {
+ tm->tm_mon = 0;
+ tm->tm_year++;
+ }
+ }
+ }
+ }
+ }
+}
+
+/*
+ * tick handler
+ */
+static void exynos4210_rtc_tick(void *opaque)
+{
+ Exynos4210RTCState *s = (Exynos4210RTCState *)opaque;
+
+ DPRINTF("TICK IRQ\n");
+ /* set irq status */
+ s->reg_intp |= INTP_TICK_ENABLE;
+ /* raise IRQ */
+ qemu_irq_raise(s->tick_irq);
+
+ /* restart timer */
+ ptimer_set_count(s->ptimer, s->reg_ticcnt);
+ ptimer_run(s->ptimer, 1);
+}
+
+/*
+ * 1Hz clock handler
+ */
+static void exynos4210_rtc_1Hz_tick(void *opaque)
+{
+ Exynos4210RTCState *s = (Exynos4210RTCState *)opaque;
+
+ rtc_next_second(&s->current_tm);
+ /* DPRINTF("1Hz tick\n"); */
+
+ /* raise IRQ */
+ if (s->reg_rtcalm & ALARM_INT_ENABLE) {
+ check_alarm_raise(s);
+ }
+
+ ptimer_set_count(s->ptimer_1Hz, RTC_BASE_FREQ);
+ ptimer_run(s->ptimer_1Hz, 1);
+}
+
+/*
+ * RTC Read
+ */
+static uint64_t exynos4210_rtc_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ uint32_t value = 0;
+ Exynos4210RTCState *s = (Exynos4210RTCState *)opaque;
+
+ switch (offset) {
+ case INTP:
+ value = s->reg_intp;
+ break;
+ case RTCCON:
+ value = s->reg_rtccon;
+ break;
+ case TICCNT:
+ value = s->reg_ticcnt;
+ break;
+ case RTCALM:
+ value = s->reg_rtcalm;
+ break;
+ case ALMSEC:
+ value = s->reg_almsec;
+ break;
+ case ALMMIN:
+ value = s->reg_almmin;
+ break;
+ case ALMHOUR:
+ value = s->reg_almhour;
+ break;
+ case ALMDAY:
+ value = s->reg_almday;
+ break;
+ case ALMMON:
+ value = s->reg_almmon;
+ break;
+ case ALMYEAR:
+ value = s->reg_almyear;
+ break;
+
+ case BCDSEC:
+ value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_sec);
+ break;
+ case BCDMIN:
+ value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_min);
+ break;
+ case BCDHOUR:
+ value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_hour);
+ break;
+ case BCDDAYWEEK:
+ value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_wday);
+ break;
+ case BCDDAY:
+ value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_mday);
+ break;
+ case BCDMON:
+ value = (uint32_t)to_bcd((uint8_t)s->current_tm.tm_mon + 1);
+ break;
+ case BCDYEAR:
+ value = BCD3DIGITS(s->current_tm.tm_year);
+ break;
+
+ case CURTICNT:
+ s->reg_curticcnt = ptimer_get_count(s->ptimer);
+ value = s->reg_curticcnt;
+ break;
+
+ default:
+ fprintf(stderr,
+ "[exynos4210.rtc: bad read offset " TARGET_FMT_plx "]\n",
+ offset);
+ break;
+ }
+ return value;
+}
+
+/*
+ * RTC Write
+ */
+static void exynos4210_rtc_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ Exynos4210RTCState *s = (Exynos4210RTCState *)opaque;
+
+ switch (offset) {
+ case INTP:
+ if (value & INTP_ALM_ENABLE) {
+ qemu_irq_lower(s->alm_irq);
+ s->reg_intp &= (~INTP_ALM_ENABLE);
+ }
+ if (value & INTP_TICK_ENABLE) {
+ qemu_irq_lower(s->tick_irq);
+ s->reg_intp &= (~INTP_TICK_ENABLE);
+ }
+ break;
+ case RTCCON:
+ if (value & RTC_ENABLE) {
+ exynos4210_rtc_update_freq(s, value);
+ }
+ if ((value & RTC_ENABLE) > (s->reg_rtccon & RTC_ENABLE)) {
+ /* clock timer */
+ ptimer_set_count(s->ptimer_1Hz, RTC_BASE_FREQ);
+ ptimer_run(s->ptimer_1Hz, 1);
+ DPRINTF("run clock timer\n");
+ }
+ if ((value & RTC_ENABLE) < (s->reg_rtccon & RTC_ENABLE)) {
+ /* tick timer */
+ ptimer_stop(s->ptimer);
+ /* clock timer */
+ ptimer_stop(s->ptimer_1Hz);
+ DPRINTF("stop all timers\n");
+ }
+ if (value & RTC_ENABLE) {
+ if ((value & TICK_TIMER_ENABLE) >
+ (s->reg_rtccon & TICK_TIMER_ENABLE) &&
+ (s->reg_ticcnt)) {
+ ptimer_set_count(s->ptimer, s->reg_ticcnt);
+ ptimer_run(s->ptimer, 1);
+ DPRINTF("run tick timer\n");
+ }
+ if ((value & TICK_TIMER_ENABLE) <
+ (s->reg_rtccon & TICK_TIMER_ENABLE)) {
+ ptimer_stop(s->ptimer);
+ }
+ }
+ s->reg_rtccon = value;
+ break;
+ case TICCNT:
+ if (value > TICNT_THRESHHOLD) {
+ s->reg_ticcnt = value;
+ } else {
+ fprintf(stderr,
+ "[exynos4210.rtc: bad TICNT value %u ]\n",
+ (uint32_t)value);
+ }
+ break;
+
+ case RTCALM:
+ s->reg_rtcalm = value;
+ break;
+ case ALMSEC:
+ s->reg_almsec = (value & 0x7f);
+ break;
+ case ALMMIN:
+ s->reg_almmin = (value & 0x7f);
+ break;
+ case ALMHOUR:
+ s->reg_almhour = (value & 0x3f);
+ break;
+ case ALMDAY:
+ s->reg_almday = (value & 0x3f);
+ break;
+ case ALMMON:
+ s->reg_almmon = (value & 0x1f);
+ break;
+ case ALMYEAR:
+ s->reg_almyear = (value & 0x0fff);
+ break;
+
+ case BCDSEC:
+ if (s->reg_rtccon & RTC_ENABLE) {
+ s->current_tm.tm_sec = (int)from_bcd((uint8_t)value);
+ }
+ break;
+ case BCDMIN:
+ if (s->reg_rtccon & RTC_ENABLE) {
+ s->current_tm.tm_min = (int)from_bcd((uint8_t)value);
+ }
+ break;
+ case BCDHOUR:
+ if (s->reg_rtccon & RTC_ENABLE) {
+ s->current_tm.tm_hour = (int)from_bcd((uint8_t)value);
+ }
+ break;
+ case BCDDAYWEEK:
+ if (s->reg_rtccon & RTC_ENABLE) {
+ s->current_tm.tm_wday = (int)from_bcd((uint8_t)value);
+ }
+ break;
+ case BCDDAY:
+ if (s->reg_rtccon & RTC_ENABLE) {
+ s->current_tm.tm_mday = (int)from_bcd((uint8_t)value);
+ }
+ break;
+ case BCDMON:
+ if (s->reg_rtccon & RTC_ENABLE) {
+ s->current_tm.tm_mon = (int)from_bcd((uint8_t)value) - 1;
+ }
+ break;
+ case BCDYEAR:
+ if (s->reg_rtccon & RTC_ENABLE) {
+ /* 3 digits */
+ s->current_tm.tm_year = (int)from_bcd((uint8_t)value) +
+ (int)from_bcd((uint8_t)((value >> 8) & 0x0f)) * 100;
+ }
+ break;
+
+ default:
+ fprintf(stderr,
+ "[exynos4210.rtc: bad write offset " TARGET_FMT_plx "]\n",
+ offset);
+ break;
+
+ }
+}
+
+/*
+ * Set default values to timer fields and registers
+ */
+static void exynos4210_rtc_reset(DeviceState *d)
+{
+ Exynos4210RTCState *s = (Exynos4210RTCState *)d;
+
+ qemu_get_timedate(&s->current_tm, 0);
+
+ DPRINTF("Get time from host: %d-%d-%d %2d:%02d:%02d\n",
+ s->current_tm.tm_year, s->current_tm.tm_mon, s->current_tm.tm_mday,
+ s->current_tm.tm_hour, s->current_tm.tm_min, s->current_tm.tm_sec);
+
+ s->reg_intp = 0;
+ s->reg_rtccon = 0;
+ s->reg_ticcnt = 0;
+ s->reg_rtcalm = 0;
+ s->reg_almsec = 0;
+ s->reg_almmin = 0;
+ s->reg_almhour = 0;
+ s->reg_almday = 0;
+ s->reg_almmon = 0;
+ s->reg_almyear = 0;
+
+ s->reg_curticcnt = 0;
+
+ exynos4210_rtc_update_freq(s, s->reg_rtccon);
+ ptimer_stop(s->ptimer);
+ ptimer_stop(s->ptimer_1Hz);
+}
+
+static const MemoryRegionOps exynos4210_rtc_ops = {
+ .read = exynos4210_rtc_read,
+ .write = exynos4210_rtc_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+/*
+ * RTC timer initialization
+ */
+static int exynos4210_rtc_init(SysBusDevice *dev)
+{
+ Exynos4210RTCState *s = FROM_SYSBUS(Exynos4210RTCState, dev);
+ QEMUBH *bh;
+
+ bh = qemu_bh_new(exynos4210_rtc_tick, s);
+ s->ptimer = ptimer_init(bh);
+ ptimer_set_freq(s->ptimer, RTC_BASE_FREQ);
+ exynos4210_rtc_update_freq(s, 0);
+
+ bh = qemu_bh_new(exynos4210_rtc_1Hz_tick, s);
+ s->ptimer_1Hz = ptimer_init(bh);
+ ptimer_set_freq(s->ptimer_1Hz, RTC_BASE_FREQ);
+
+ sysbus_init_irq(dev, &s->alm_irq);
+ sysbus_init_irq(dev, &s->tick_irq);
+
+ memory_region_init_io(&s->iomem, &exynos4210_rtc_ops, s, "exynos4210-rtc",
+ EXYNOS4210_RTC_REG_MEM_SIZE);
+ sysbus_init_mmio(dev, &s->iomem);
+
+ return 0;
+}
+
+static void exynos4210_rtc_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = exynos4210_rtc_init;
+ dc->reset = exynos4210_rtc_reset;
+ dc->vmsd = &vmstate_exynos4210_rtc_state;
+}
+
+static const TypeInfo exynos4210_rtc_info = {
+ .name = "exynos4210.rtc",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(Exynos4210RTCState),
+ .class_init = exynos4210_rtc_class_init,
+};
+
+static void exynos4210_rtc_register_types(void)
+{
+ type_register_static(&exynos4210_rtc_info);
+}
+
+type_init(exynos4210_rtc_register_types)
--- /dev/null
+/*
+ * QEMU GRLIB GPTimer Emulator
+ *
+ * Copyright (c) 2010-2011 AdaCore
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw/sysbus.h"
+#include "qemu/timer.h"
+#include "hw/ptimer.h"
+
+#include "trace.h"
+
+#define UNIT_REG_SIZE 16 /* Size of memory mapped regs for the unit */
+#define GPTIMER_REG_SIZE 16 /* Size of memory mapped regs for a GPTimer */
+
+#define GPTIMER_MAX_TIMERS 8
+
+/* GPTimer Config register fields */
+#define GPTIMER_ENABLE (1 << 0)
+#define GPTIMER_RESTART (1 << 1)
+#define GPTIMER_LOAD (1 << 2)
+#define GPTIMER_INT_ENABLE (1 << 3)
+#define GPTIMER_INT_PENDING (1 << 4)
+#define GPTIMER_CHAIN (1 << 5) /* Not supported */
+#define GPTIMER_DEBUG_HALT (1 << 6) /* Not supported */
+
+/* Memory mapped register offsets */
+#define SCALER_OFFSET 0x00
+#define SCALER_RELOAD_OFFSET 0x04
+#define CONFIG_OFFSET 0x08
+#define COUNTER_OFFSET 0x00
+#define COUNTER_RELOAD_OFFSET 0x04
+#define TIMER_BASE 0x10
+
+typedef struct GPTimer GPTimer;
+typedef struct GPTimerUnit GPTimerUnit;
+
+struct GPTimer {
+ QEMUBH *bh;
+ struct ptimer_state *ptimer;
+
+ qemu_irq irq;
+ int id;
+ GPTimerUnit *unit;
+
+ /* registers */
+ uint32_t counter;
+ uint32_t reload;
+ uint32_t config;
+};
+
+struct GPTimerUnit {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+
+ uint32_t nr_timers; /* Number of timers available */
+ uint32_t freq_hz; /* System frequency */
+ uint32_t irq_line; /* Base irq line */
+
+ GPTimer *timers;
+
+ /* registers */
+ uint32_t scaler;
+ uint32_t reload;
+ uint32_t config;
+};
+
+static void grlib_gptimer_enable(GPTimer *timer)
+{
+ assert(timer != NULL);
+
+
+ ptimer_stop(timer->ptimer);
+
+ if (!(timer->config & GPTIMER_ENABLE)) {
+ /* Timer disabled */
+ trace_grlib_gptimer_disabled(timer->id, timer->config);
+ return;
+ }
+
+ /* ptimer is triggered when the counter reach 0 but GPTimer is triggered at
+ underflow. Set count + 1 to simulate the GPTimer behavior. */
+
+ trace_grlib_gptimer_enable(timer->id, timer->counter + 1);
+
+ ptimer_set_count(timer->ptimer, timer->counter + 1);
+ ptimer_run(timer->ptimer, 1);
+}
+
+static void grlib_gptimer_restart(GPTimer *timer)
+{
+ assert(timer != NULL);
+
+ trace_grlib_gptimer_restart(timer->id, timer->reload);
+
+ timer->counter = timer->reload;
+ grlib_gptimer_enable(timer);
+}
+
+static void grlib_gptimer_set_scaler(GPTimerUnit *unit, uint32_t scaler)
+{
+ int i = 0;
+ uint32_t value = 0;
+
+ assert(unit != NULL);
+
+ if (scaler > 0) {
+ value = unit->freq_hz / (scaler + 1);
+ } else {
+ value = unit->freq_hz;
+ }
+
+ trace_grlib_gptimer_set_scaler(scaler, value);
+
+ for (i = 0; i < unit->nr_timers; i++) {
+ ptimer_set_freq(unit->timers[i].ptimer, value);
+ }
+}
+
+static void grlib_gptimer_hit(void *opaque)
+{
+ GPTimer *timer = opaque;
+ assert(timer != NULL);
+
+ trace_grlib_gptimer_hit(timer->id);
+
+ /* Timer expired */
+
+ if (timer->config & GPTIMER_INT_ENABLE) {
+ /* Set the pending bit (only unset by write in the config register) */
+ timer->config |= GPTIMER_INT_PENDING;
+ qemu_irq_pulse(timer->irq);
+ }
+
+ if (timer->config & GPTIMER_RESTART) {
+ grlib_gptimer_restart(timer);
+ }
+}
+
+static uint64_t grlib_gptimer_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ GPTimerUnit *unit = opaque;
+ hwaddr timer_addr;
+ int id;
+ uint32_t value = 0;
+
+ addr &= 0xff;
+
+ /* Unit registers */
+ switch (addr) {
+ case SCALER_OFFSET:
+ trace_grlib_gptimer_readl(-1, addr, unit->scaler);
+ return unit->scaler;
+
+ case SCALER_RELOAD_OFFSET:
+ trace_grlib_gptimer_readl(-1, addr, unit->reload);
+ return unit->reload;
+
+ case CONFIG_OFFSET:
+ trace_grlib_gptimer_readl(-1, addr, unit->config);
+ return unit->config;
+
+ default:
+ break;
+ }
+
+ timer_addr = (addr % TIMER_BASE);
+ id = (addr - TIMER_BASE) / TIMER_BASE;
+
+ if (id >= 0 && id < unit->nr_timers) {
+
+ /* GPTimer registers */
+ switch (timer_addr) {
+ case COUNTER_OFFSET:
+ value = ptimer_get_count(unit->timers[id].ptimer);
+ trace_grlib_gptimer_readl(id, addr, value);
+ return value;
+
+ case COUNTER_RELOAD_OFFSET:
+ value = unit->timers[id].reload;
+ trace_grlib_gptimer_readl(id, addr, value);
+ return value;
+
+ case CONFIG_OFFSET:
+ trace_grlib_gptimer_readl(id, addr, unit->timers[id].config);
+ return unit->timers[id].config;
+
+ default:
+ break;
+ }
+
+ }
+
+ trace_grlib_gptimer_readl(-1, addr, 0);
+ return 0;
+}
+
+static void grlib_gptimer_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ GPTimerUnit *unit = opaque;
+ hwaddr timer_addr;
+ int id;
+
+ addr &= 0xff;
+
+ /* Unit registers */
+ switch (addr) {
+ case SCALER_OFFSET:
+ value &= 0xFFFF; /* clean up the value */
+ unit->scaler = value;
+ trace_grlib_gptimer_writel(-1, addr, unit->scaler);
+ return;
+
+ case SCALER_RELOAD_OFFSET:
+ value &= 0xFFFF; /* clean up the value */
+ unit->reload = value;
+ trace_grlib_gptimer_writel(-1, addr, unit->reload);
+ grlib_gptimer_set_scaler(unit, value);
+ return;
+
+ case CONFIG_OFFSET:
+ /* Read Only (disable timer freeze not supported) */
+ trace_grlib_gptimer_writel(-1, addr, 0);
+ return;
+
+ default:
+ break;
+ }
+
+ timer_addr = (addr % TIMER_BASE);
+ id = (addr - TIMER_BASE) / TIMER_BASE;
+
+ if (id >= 0 && id < unit->nr_timers) {
+
+ /* GPTimer registers */
+ switch (timer_addr) {
+ case COUNTER_OFFSET:
+ trace_grlib_gptimer_writel(id, addr, value);
+ unit->timers[id].counter = value;
+ grlib_gptimer_enable(&unit->timers[id]);
+ return;
+
+ case COUNTER_RELOAD_OFFSET:
+ trace_grlib_gptimer_writel(id, addr, value);
+ unit->timers[id].reload = value;
+ return;
+
+ case CONFIG_OFFSET:
+ trace_grlib_gptimer_writel(id, addr, value);
+
+ if (value & GPTIMER_INT_PENDING) {
+ /* clear pending bit */
+ value &= ~GPTIMER_INT_PENDING;
+ } else {
+ /* keep pending bit */
+ value |= unit->timers[id].config & GPTIMER_INT_PENDING;
+ }
+
+ unit->timers[id].config = value;
+
+ /* gptimer_restart calls gptimer_enable, so if "enable" and "load"
+ bits are present, we just have to call restart. */
+
+ if (value & GPTIMER_LOAD) {
+ grlib_gptimer_restart(&unit->timers[id]);
+ } else if (value & GPTIMER_ENABLE) {
+ grlib_gptimer_enable(&unit->timers[id]);
+ }
+
+ /* These fields must always be read as 0 */
+ value &= ~(GPTIMER_LOAD & GPTIMER_DEBUG_HALT);
+
+ unit->timers[id].config = value;
+ return;
+
+ default:
+ break;
+ }
+
+ }
+
+ trace_grlib_gptimer_writel(-1, addr, value);
+}
+
+static const MemoryRegionOps grlib_gptimer_ops = {
+ .read = grlib_gptimer_read,
+ .write = grlib_gptimer_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+};
+
+static void grlib_gptimer_reset(DeviceState *d)
+{
+ GPTimerUnit *unit = container_of(d, GPTimerUnit, busdev.qdev);
+ int i = 0;
+
+ assert(unit != NULL);
+
+ unit->scaler = 0;
+ unit->reload = 0;
+ unit->config = 0;
+
+ unit->config = unit->nr_timers;
+ unit->config |= unit->irq_line << 3;
+ unit->config |= 1 << 8; /* separate interrupt */
+ unit->config |= 1 << 9; /* Disable timer freeze */
+
+
+ for (i = 0; i < unit->nr_timers; i++) {
+ GPTimer *timer = &unit->timers[i];
+
+ timer->counter = 0;
+ timer->reload = 0;
+ timer->config = 0;
+ ptimer_stop(timer->ptimer);
+ ptimer_set_count(timer->ptimer, 0);
+ ptimer_set_freq(timer->ptimer, unit->freq_hz);
+ }
+}
+
+static int grlib_gptimer_init(SysBusDevice *dev)
+{
+ GPTimerUnit *unit = FROM_SYSBUS(typeof(*unit), dev);
+ unsigned int i;
+
+ assert(unit->nr_timers > 0);
+ assert(unit->nr_timers <= GPTIMER_MAX_TIMERS);
+
+ unit->timers = g_malloc0(sizeof unit->timers[0] * unit->nr_timers);
+
+ for (i = 0; i < unit->nr_timers; i++) {
+ GPTimer *timer = &unit->timers[i];
+
+ timer->unit = unit;
+ timer->bh = qemu_bh_new(grlib_gptimer_hit, timer);
+ timer->ptimer = ptimer_init(timer->bh);
+ timer->id = i;
+
+ /* One IRQ line for each timer */
+ sysbus_init_irq(dev, &timer->irq);
+
+ ptimer_set_freq(timer->ptimer, unit->freq_hz);
+ }
+
+ memory_region_init_io(&unit->iomem, &grlib_gptimer_ops, unit, "gptimer",
+ UNIT_REG_SIZE + GPTIMER_REG_SIZE * unit->nr_timers);
+
+ sysbus_init_mmio(dev, &unit->iomem);
+ return 0;
+}
+
+static Property grlib_gptimer_properties[] = {
+ DEFINE_PROP_UINT32("frequency", GPTimerUnit, freq_hz, 40000000),
+ DEFINE_PROP_UINT32("irq-line", GPTimerUnit, irq_line, 8),
+ DEFINE_PROP_UINT32("nr-timers", GPTimerUnit, nr_timers, 2),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void grlib_gptimer_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = grlib_gptimer_init;
+ dc->reset = grlib_gptimer_reset;
+ dc->props = grlib_gptimer_properties;
+}
+
+static const TypeInfo grlib_gptimer_info = {
+ .name = "grlib,gptimer",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(GPTimerUnit),
+ .class_init = grlib_gptimer_class_init,
+};
+
+static void grlib_gptimer_register_types(void)
+{
+ type_register_static(&grlib_gptimer_info);
+}
+
+type_init(grlib_gptimer_register_types)
--- /dev/null
+/*
+ * IMX31 Timer
+ *
+ * Copyright (c) 2008 OK Labs
+ * Copyright (c) 2011 NICTA Pty Ltd
+ * Originally written by Hans Jiang
+ * Updated by Peter Chubb
+ *
+ * This code is licensed under GPL version 2 or later. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include "hw/hw.h"
+#include "qemu/timer.h"
+#include "hw/ptimer.h"
+#include "hw/sysbus.h"
+#include "hw/arm/imx.h"
+
+//#define DEBUG_TIMER 1
+#ifdef DEBUG_TIMER
+# define DPRINTF(fmt, args...) \
+ do { printf("imx_timer: " fmt , ##args); } while (0)
+#else
+# define DPRINTF(fmt, args...) do {} while (0)
+#endif
+
+/*
+ * Define to 1 for messages about attempts to
+ * access unimplemented registers or similar.
+ */
+#define DEBUG_IMPLEMENTATION 1
+#if DEBUG_IMPLEMENTATION
+# define IPRINTF(fmt, args...) \
+ do { fprintf(stderr, "imx_timer: " fmt, ##args); } while (0)
+#else
+# define IPRINTF(fmt, args...) do {} while (0)
+#endif
+
+/*
+ * GPT : General purpose timer
+ *
+ * This timer counts up continuously while it is enabled, resetting itself
+ * to 0 when it reaches TIMER_MAX (in freerun mode) or when it
+ * reaches the value of ocr1 (in periodic mode). WE simulate this using a
+ * QEMU ptimer counting down from ocr1 and reloading from ocr1 in
+ * periodic mode, or counting from ocr1 to zero, then TIMER_MAX - ocr1.
+ * waiting_rov is set when counting from TIMER_MAX.
+ *
+ * In the real hardware, there are three comparison registers that can
+ * trigger interrupts, and compare channel 1 can be used to
+ * force-reset the timer. However, this is a `bare-bones'
+ * implementation: only what Linux 3.x uses has been implemented
+ * (free-running timer from 0 to OCR1 or TIMER_MAX) .
+ */
+
+
+#define TIMER_MAX 0XFFFFFFFFUL
+
+/* Control register. Not all of these bits have any effect (yet) */
+#define GPT_CR_EN (1 << 0) /* GPT Enable */
+#define GPT_CR_ENMOD (1 << 1) /* GPT Enable Mode */
+#define GPT_CR_DBGEN (1 << 2) /* GPT Debug mode enable */
+#define GPT_CR_WAITEN (1 << 3) /* GPT Wait Mode Enable */
+#define GPT_CR_DOZEN (1 << 4) /* GPT Doze mode enable */
+#define GPT_CR_STOPEN (1 << 5) /* GPT Stop Mode Enable */
+#define GPT_CR_CLKSRC_SHIFT (6)
+#define GPT_CR_CLKSRC_MASK (0x7)
+
+#define GPT_CR_FRR (1 << 9) /* Freerun or Restart */
+#define GPT_CR_SWR (1 << 15) /* Software Reset */
+#define GPT_CR_IM1 (3 << 16) /* Input capture channel 1 mode (2 bits) */
+#define GPT_CR_IM2 (3 << 18) /* Input capture channel 2 mode (2 bits) */
+#define GPT_CR_OM1 (7 << 20) /* Output Compare Channel 1 Mode (3 bits) */
+#define GPT_CR_OM2 (7 << 23) /* Output Compare Channel 2 Mode (3 bits) */
+#define GPT_CR_OM3 (7 << 26) /* Output Compare Channel 3 Mode (3 bits) */
+#define GPT_CR_FO1 (1 << 29) /* Force Output Compare Channel 1 */
+#define GPT_CR_FO2 (1 << 30) /* Force Output Compare Channel 2 */
+#define GPT_CR_FO3 (1 << 31) /* Force Output Compare Channel 3 */
+
+#define GPT_SR_OF1 (1 << 0)
+#define GPT_SR_ROV (1 << 5)
+
+#define GPT_IR_OF1IE (1 << 0)
+#define GPT_IR_ROVIE (1 << 5)
+
+typedef struct {
+ SysBusDevice busdev;
+ ptimer_state *timer;
+ MemoryRegion iomem;
+ DeviceState *ccm;
+
+ uint32_t cr;
+ uint32_t pr;
+ uint32_t sr;
+ uint32_t ir;
+ uint32_t ocr1;
+ uint32_t cnt;
+
+ uint32_t waiting_rov;
+ qemu_irq irq;
+} IMXTimerGState;
+
+static const VMStateDescription vmstate_imx_timerg = {
+ .name = "imx-timerg",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(cr, IMXTimerGState),
+ VMSTATE_UINT32(pr, IMXTimerGState),
+ VMSTATE_UINT32(sr, IMXTimerGState),
+ VMSTATE_UINT32(ir, IMXTimerGState),
+ VMSTATE_UINT32(ocr1, IMXTimerGState),
+ VMSTATE_UINT32(cnt, IMXTimerGState),
+ VMSTATE_UINT32(waiting_rov, IMXTimerGState),
+ VMSTATE_PTIMER(timer, IMXTimerGState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const IMXClk imx_timerg_clocks[] = {
+ NOCLK, /* 000 No clock source */
+ IPG, /* 001 ipg_clk, 532MHz*/
+ IPG, /* 010 ipg_clk_highfreq */
+ NOCLK, /* 011 not defined */
+ CLK_32k, /* 100 ipg_clk_32k */
+ NOCLK, /* 101 not defined */
+ NOCLK, /* 110 not defined */
+ NOCLK, /* 111 not defined */
+};
+
+
+static void imx_timerg_set_freq(IMXTimerGState *s)
+{
+ int clksrc;
+ uint32_t freq;
+
+ clksrc = (s->cr >> GPT_CR_CLKSRC_SHIFT) & GPT_CR_CLKSRC_MASK;
+ freq = imx_clock_frequency(s->ccm, imx_timerg_clocks[clksrc]) / (1 + s->pr);
+
+ DPRINTF("Setting gtimer clksrc %d to frequency %d\n", clksrc, freq);
+ if (freq) {
+ ptimer_set_freq(s->timer, freq);
+ }
+}
+
+static void imx_timerg_update(IMXTimerGState *s)
+{
+ uint32_t flags = s->sr & s->ir & (GPT_SR_OF1 | GPT_SR_ROV);
+
+ DPRINTF("g-timer SR: %s %s IR=%s %s, %s\n",
+ s->sr & GPT_SR_OF1 ? "OF1" : "",
+ s->sr & GPT_SR_ROV ? "ROV" : "",
+ s->ir & GPT_SR_OF1 ? "OF1" : "",
+ s->ir & GPT_SR_ROV ? "ROV" : "",
+ s->cr & GPT_CR_EN ? "CR_EN" : "Not Enabled");
+
+
+ qemu_set_irq(s->irq, (s->cr & GPT_CR_EN) && flags);
+}
+
+static uint32_t imx_timerg_update_counts(IMXTimerGState *s)
+{
+ uint64_t target = s->waiting_rov ? TIMER_MAX : s->ocr1;
+ uint64_t cnt = ptimer_get_count(s->timer);
+ s->cnt = target - cnt;
+ return s->cnt;
+}
+
+static void imx_timerg_reload(IMXTimerGState *s, uint32_t timeout)
+{
+ uint64_t diff_cnt;
+
+ if (!(s->cr & GPT_CR_FRR)) {
+ IPRINTF("IMX_timerg_reload --- called in reset-mode\n");
+ return;
+ }
+
+ /*
+ * For small timeouts, qemu sometimes runs too slow.
+ * Better deliver a late interrupt than none.
+ *
+ * In Reset mode (FRR bit clear)
+ * the ptimer reloads itself from OCR1;
+ * in free-running mode we need to fake
+ * running from 0 to ocr1 to TIMER_MAX
+ */
+ if (timeout > s->cnt) {
+ diff_cnt = timeout - s->cnt;
+ } else {
+ diff_cnt = 0;
+ }
+ ptimer_set_count(s->timer, diff_cnt);
+}
+
+static uint64_t imx_timerg_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ IMXTimerGState *s = (IMXTimerGState *)opaque;
+
+ DPRINTF("g-read(offset=%x)", offset >> 2);
+ switch (offset >> 2) {
+ case 0: /* Control Register */
+ DPRINTF(" cr = %x\n", s->cr);
+ return s->cr;
+
+ case 1: /* prescaler */
+ DPRINTF(" pr = %x\n", s->pr);
+ return s->pr;
+
+ case 2: /* Status Register */
+ DPRINTF(" sr = %x\n", s->sr);
+ return s->sr;
+
+ case 3: /* Interrupt Register */
+ DPRINTF(" ir = %x\n", s->ir);
+ return s->ir;
+
+ case 4: /* Output Compare Register 1 */
+ DPRINTF(" ocr1 = %x\n", s->ocr1);
+ return s->ocr1;
+
+
+ case 9: /* cnt */
+ imx_timerg_update_counts(s);
+ DPRINTF(" cnt = %x\n", s->cnt);
+ return s->cnt;
+ }
+
+ IPRINTF("imx_timerg_read: Bad offset %x\n",
+ (int)offset >> 2);
+ return 0;
+}
+
+static void imx_timerg_reset(DeviceState *dev)
+{
+ IMXTimerGState *s = container_of(dev, IMXTimerGState, busdev.qdev);
+
+ /*
+ * Soft reset doesn't touch some bits; hard reset clears them
+ */
+ s->cr &= ~(GPT_CR_EN|GPT_CR_DOZEN|GPT_CR_WAITEN|GPT_CR_DBGEN);
+ s->sr = 0;
+ s->pr = 0;
+ s->ir = 0;
+ s->cnt = 0;
+ s->ocr1 = TIMER_MAX;
+ ptimer_stop(s->timer);
+ ptimer_set_limit(s->timer, TIMER_MAX, 1);
+ imx_timerg_set_freq(s);
+}
+
+static void imx_timerg_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ IMXTimerGState *s = (IMXTimerGState *)opaque;
+ DPRINTF("g-write(offset=%x, value = 0x%x)\n", (unsigned int)offset >> 2,
+ (unsigned int)value);
+
+ switch (offset >> 2) {
+ case 0: {
+ uint32_t oldcr = s->cr;
+ /* CR */
+ if (value & GPT_CR_SWR) { /* force reset */
+ value &= ~GPT_CR_SWR;
+ imx_timerg_reset(&s->busdev.qdev);
+ imx_timerg_update(s);
+ }
+
+ s->cr = value & ~0x7c00;
+ imx_timerg_set_freq(s);
+ if ((oldcr ^ value) & GPT_CR_EN) {
+ if (value & GPT_CR_EN) {
+ if (value & GPT_CR_ENMOD) {
+ ptimer_set_count(s->timer, s->ocr1);
+ s->cnt = 0;
+ }
+ ptimer_run(s->timer,
+ (value & GPT_CR_FRR) && (s->ocr1 != TIMER_MAX));
+ } else {
+ ptimer_stop(s->timer);
+ };
+ }
+ return;
+ }
+
+ case 1: /* Prescaler */
+ s->pr = value & 0xfff;
+ imx_timerg_set_freq(s);
+ return;
+
+ case 2: /* SR */
+ /*
+ * No point in implementing the status register bits to do with
+ * external interrupt sources.
+ */
+ value &= GPT_SR_OF1 | GPT_SR_ROV;
+ s->sr &= ~value;
+ imx_timerg_update(s);
+ return;
+
+ case 3: /* IR -- interrupt register */
+ s->ir = value & 0x3f;
+ imx_timerg_update(s);
+ return;
+
+ case 4: /* OCR1 -- output compare register */
+ /* In non-freerun mode, reset count when this register is written */
+ if (!(s->cr & GPT_CR_FRR)) {
+ s->waiting_rov = 0;
+ ptimer_set_limit(s->timer, value, 1);
+ } else {
+ imx_timerg_update_counts(s);
+ if (value > s->cnt) {
+ s->waiting_rov = 0;
+ imx_timerg_reload(s, value);
+ } else {
+ s->waiting_rov = 1;
+ imx_timerg_reload(s, TIMER_MAX - s->cnt);
+ }
+ }
+ s->ocr1 = value;
+ return;
+
+ default:
+ IPRINTF("imx_timerg_write: Bad offset %x\n",
+ (int)offset >> 2);
+ }
+}
+
+static void imx_timerg_timeout(void *opaque)
+{
+ IMXTimerGState *s = (IMXTimerGState *)opaque;
+
+ DPRINTF("imx_timerg_timeout, waiting rov=%d\n", s->waiting_rov);
+ if (s->cr & GPT_CR_FRR) {
+ /*
+ * Free running timer from 0 -> TIMERMAX
+ * Generates interrupt at TIMER_MAX and at cnt==ocr1
+ * If ocr1 == TIMER_MAX, then no need to reload timer.
+ */
+ if (s->ocr1 == TIMER_MAX) {
+ DPRINTF("s->ocr1 == TIMER_MAX, FRR\n");
+ s->sr |= GPT_SR_OF1 | GPT_SR_ROV;
+ imx_timerg_update(s);
+ return;
+ }
+
+ if (s->waiting_rov) {
+ /*
+ * We were waiting for cnt==TIMER_MAX
+ */
+ s->sr |= GPT_SR_ROV;
+ s->waiting_rov = 0;
+ s->cnt = 0;
+ imx_timerg_reload(s, s->ocr1);
+ } else {
+ /* Must have got a cnt==ocr1 timeout. */
+ s->sr |= GPT_SR_OF1;
+ s->cnt = s->ocr1;
+ s->waiting_rov = 1;
+ imx_timerg_reload(s, TIMER_MAX);
+ }
+ imx_timerg_update(s);
+ return;
+ }
+
+ s->sr |= GPT_SR_OF1;
+ imx_timerg_update(s);
+}
+
+static const MemoryRegionOps imx_timerg_ops = {
+ .read = imx_timerg_read,
+ .write = imx_timerg_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+
+static int imx_timerg_init(SysBusDevice *dev)
+{
+ IMXTimerGState *s = FROM_SYSBUS(IMXTimerGState, dev);
+ QEMUBH *bh;
+
+ sysbus_init_irq(dev, &s->irq);
+ memory_region_init_io(&s->iomem, &imx_timerg_ops,
+ s, "imxg-timer",
+ 0x00001000);
+ sysbus_init_mmio(dev, &s->iomem);
+
+ bh = qemu_bh_new(imx_timerg_timeout, s);
+ s->timer = ptimer_init(bh);
+
+ /* Hard reset resets extra bits in CR */
+ s->cr = 0;
+ return 0;
+}
+
+
+
+/*
+ * EPIT: Enhanced periodic interrupt timer
+ */
+
+#define CR_EN (1 << 0)
+#define CR_ENMOD (1 << 1)
+#define CR_OCIEN (1 << 2)
+#define CR_RLD (1 << 3)
+#define CR_PRESCALE_SHIFT (4)
+#define CR_PRESCALE_MASK (0xfff)
+#define CR_SWR (1 << 16)
+#define CR_IOVW (1 << 17)
+#define CR_DBGEN (1 << 18)
+#define CR_EPIT (1 << 19)
+#define CR_DOZEN (1 << 20)
+#define CR_STOPEN (1 << 21)
+#define CR_CLKSRC_SHIFT (24)
+#define CR_CLKSRC_MASK (0x3 << CR_CLKSRC_SHIFT)
+
+
+/*
+ * Exact clock frequencies vary from board to board.
+ * These are typical.
+ */
+static const IMXClk imx_timerp_clocks[] = {
+ 0, /* disabled */
+ IPG, /* ipg_clk, ~532MHz */
+ IPG, /* ipg_clk_highfreq */
+ CLK_32k, /* ipg_clk_32k -- ~32kHz */
+};
+
+typedef struct {
+ SysBusDevice busdev;
+ ptimer_state *timer;
+ MemoryRegion iomem;
+ DeviceState *ccm;
+
+ uint32_t cr;
+ uint32_t lr;
+ uint32_t cmp;
+
+ uint32_t freq;
+ int int_level;
+ qemu_irq irq;
+} IMXTimerPState;
+
+/*
+ * Update interrupt status
+ */
+static void imx_timerp_update(IMXTimerPState *s)
+{
+ if (s->int_level && (s->cr & CR_OCIEN)) {
+ qemu_irq_raise(s->irq);
+ } else {
+ qemu_irq_lower(s->irq);
+ }
+}
+
+static void imx_timerp_reset(DeviceState *dev)
+{
+ IMXTimerPState *s = container_of(dev, IMXTimerPState, busdev.qdev);
+
+ s->cr = 0;
+ s->lr = TIMER_MAX;
+ s->int_level = 0;
+ s->cmp = 0;
+ ptimer_stop(s->timer);
+ ptimer_set_count(s->timer, TIMER_MAX);
+}
+
+static uint64_t imx_timerp_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ IMXTimerPState *s = (IMXTimerPState *)opaque;
+
+ DPRINTF("p-read(offset=%x)", offset >> 2);
+ switch (offset >> 2) {
+ case 0: /* Control Register */
+ DPRINTF("cr %x\n", s->cr);
+ return s->cr;
+
+ case 1: /* Status Register */
+ DPRINTF("int_level %x\n", s->int_level);
+ return s->int_level;
+
+ case 2: /* LR - ticks*/
+ DPRINTF("lr %x\n", s->lr);
+ return s->lr;
+
+ case 3: /* CMP */
+ DPRINTF("cmp %x\n", s->cmp);
+ return s->cmp;
+
+ case 4: /* CNT */
+ return ptimer_get_count(s->timer);
+ }
+ IPRINTF("imx_timerp_read: Bad offset %x\n",
+ (int)offset >> 2);
+ return 0;
+}
+
+static void set_timerp_freq(IMXTimerPState *s)
+{
+ int clksrc;
+ unsigned prescaler;
+ uint32_t freq;
+
+ clksrc = (s->cr & CR_CLKSRC_MASK) >> CR_CLKSRC_SHIFT;
+ prescaler = 1 + ((s->cr >> CR_PRESCALE_SHIFT) & CR_PRESCALE_MASK);
+ freq = imx_clock_frequency(s->ccm, imx_timerp_clocks[clksrc]) / prescaler;
+
+ s->freq = freq;
+ DPRINTF("Setting ptimer frequency to %u\n", freq);
+
+ if (freq) {
+ ptimer_set_freq(s->timer, freq);
+ }
+}
+
+static void imx_timerp_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ IMXTimerPState *s = (IMXTimerPState *)opaque;
+ DPRINTF("p-write(offset=%x, value = %x)\n", (unsigned int)offset >> 2,
+ (unsigned int)value);
+
+ switch (offset >> 2) {
+ case 0: /* CR */
+ if (value & CR_SWR) {
+ imx_timerp_reset(&s->busdev.qdev);
+ value &= ~CR_SWR;
+ }
+ s->cr = value & 0x03ffffff;
+ set_timerp_freq(s);
+
+ if (s->freq && (s->cr & CR_EN)) {
+ if (!(s->cr & CR_ENMOD)) {
+ ptimer_set_count(s->timer, s->lr);
+ }
+ ptimer_run(s->timer, 0);
+ } else {
+ ptimer_stop(s->timer);
+ }
+ break;
+
+ case 1: /* SR - ACK*/
+ s->int_level = 0;
+ imx_timerp_update(s);
+ break;
+
+ case 2: /* LR - set ticks */
+ s->lr = value;
+ ptimer_set_limit(s->timer, value, !!(s->cr & CR_IOVW));
+ break;
+
+ case 3: /* CMP */
+ s->cmp = value;
+ if (value) {
+ IPRINTF(
+ "Values for EPIT comparison other than zero not supported\n"
+ );
+ }
+ break;
+
+ default:
+ IPRINTF("imx_timerp_write: Bad offset %x\n",
+ (int)offset >> 2);
+ }
+}
+
+static void imx_timerp_tick(void *opaque)
+{
+ IMXTimerPState *s = (IMXTimerPState *)opaque;
+
+ DPRINTF("imxp tick\n");
+ if (!(s->cr & CR_RLD)) {
+ ptimer_set_count(s->timer, TIMER_MAX);
+ }
+ s->int_level = 1;
+ imx_timerp_update(s);
+}
+
+void imx_timerp_create(const hwaddr addr,
+ qemu_irq irq,
+ DeviceState *ccm)
+{
+ IMXTimerPState *pp;
+ DeviceState *dev;
+
+ dev = sysbus_create_simple("imx_timerp", addr, irq);
+ pp = container_of(dev, IMXTimerPState, busdev.qdev);
+ pp->ccm = ccm;
+}
+
+static const MemoryRegionOps imx_timerp_ops = {
+ .read = imx_timerp_read,
+ .write = imx_timerp_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static const VMStateDescription vmstate_imx_timerp = {
+ .name = "imx-timerp",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(cr, IMXTimerPState),
+ VMSTATE_UINT32(lr, IMXTimerPState),
+ VMSTATE_UINT32(cmp, IMXTimerPState),
+ VMSTATE_UINT32(freq, IMXTimerPState),
+ VMSTATE_INT32(int_level, IMXTimerPState),
+ VMSTATE_PTIMER(timer, IMXTimerPState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static int imx_timerp_init(SysBusDevice *dev)
+{
+ IMXTimerPState *s = FROM_SYSBUS(IMXTimerPState, dev);
+ QEMUBH *bh;
+
+ DPRINTF("imx_timerp_init\n");
+
+ sysbus_init_irq(dev, &s->irq);
+ memory_region_init_io(&s->iomem, &imx_timerp_ops,
+ s, "imxp-timer",
+ 0x00001000);
+ sysbus_init_mmio(dev, &s->iomem);
+
+ bh = qemu_bh_new(imx_timerp_tick, s);
+ s->timer = ptimer_init(bh);
+
+ return 0;
+}
+
+
+void imx_timerg_create(const hwaddr addr,
+ qemu_irq irq,
+ DeviceState *ccm)
+{
+ IMXTimerGState *pp;
+ DeviceState *dev;
+
+ dev = sysbus_create_simple("imx_timerg", addr, irq);
+ pp = container_of(dev, IMXTimerGState, busdev.qdev);
+ pp->ccm = ccm;
+}
+
+static void imx_timerg_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+ k->init = imx_timerg_init;
+ dc->vmsd = &vmstate_imx_timerg;
+ dc->reset = imx_timerg_reset;
+ dc->desc = "i.MX general timer";
+}
+
+static void imx_timerp_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+ k->init = imx_timerp_init;
+ dc->vmsd = &vmstate_imx_timerp;
+ dc->reset = imx_timerp_reset;
+ dc->desc = "i.MX periodic timer";
+}
+
+static const TypeInfo imx_timerp_info = {
+ .name = "imx_timerp",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(IMXTimerPState),
+ .class_init = imx_timerp_class_init,
+};
+
+static const TypeInfo imx_timerg_info = {
+ .name = "imx_timerg",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(IMXTimerGState),
+ .class_init = imx_timerg_class_init,
+};
+
+static void imx_timer_register_types(void)
+{
+ type_register_static(&imx_timerp_info);
+ type_register_static(&imx_timerg_info);
+}
+
+type_init(imx_timer_register_types)
--- /dev/null
+/*
+ * QEMU model of the LatticeMico32 timer block.
+ *
+ * Copyright (c) 2010 Michael Walle <michael@walle.cc>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * Specification available at:
+ * http://www.latticesemi.com/documents/mico32timer.pdf
+ */
+
+#include "hw/hw.h"
+#include "hw/sysbus.h"
+#include "trace.h"
+#include "qemu/timer.h"
+#include "hw/ptimer.h"
+#include "qemu/error-report.h"
+
+#define DEFAULT_FREQUENCY (50*1000000)
+
+enum {
+ R_SR = 0,
+ R_CR,
+ R_PERIOD,
+ R_SNAPSHOT,
+ R_MAX
+};
+
+enum {
+ SR_TO = (1 << 0),
+ SR_RUN = (1 << 1),
+};
+
+enum {
+ CR_ITO = (1 << 0),
+ CR_CONT = (1 << 1),
+ CR_START = (1 << 2),
+ CR_STOP = (1 << 3),
+};
+
+struct LM32TimerState {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+
+ QEMUBH *bh;
+ ptimer_state *ptimer;
+
+ qemu_irq irq;
+ uint32_t freq_hz;
+
+ uint32_t regs[R_MAX];
+};
+typedef struct LM32TimerState LM32TimerState;
+
+static void timer_update_irq(LM32TimerState *s)
+{
+ int state = (s->regs[R_SR] & SR_TO) && (s->regs[R_CR] & CR_ITO);
+
+ trace_lm32_timer_irq_state(state);
+ qemu_set_irq(s->irq, state);
+}
+
+static uint64_t timer_read(void *opaque, hwaddr addr, unsigned size)
+{
+ LM32TimerState *s = opaque;
+ uint32_t r = 0;
+
+ addr >>= 2;
+ switch (addr) {
+ case R_SR:
+ case R_CR:
+ case R_PERIOD:
+ r = s->regs[addr];
+ break;
+ case R_SNAPSHOT:
+ r = (uint32_t)ptimer_get_count(s->ptimer);
+ break;
+ default:
+ error_report("lm32_timer: read access to unknown register 0x"
+ TARGET_FMT_plx, addr << 2);
+ break;
+ }
+
+ trace_lm32_timer_memory_read(addr << 2, r);
+ return r;
+}
+
+static void timer_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ LM32TimerState *s = opaque;
+
+ trace_lm32_timer_memory_write(addr, value);
+
+ addr >>= 2;
+ switch (addr) {
+ case R_SR:
+ s->regs[R_SR] &= ~SR_TO;
+ break;
+ case R_CR:
+ s->regs[R_CR] = value;
+ if (s->regs[R_CR] & CR_START) {
+ ptimer_run(s->ptimer, 1);
+ }
+ if (s->regs[R_CR] & CR_STOP) {
+ ptimer_stop(s->ptimer);
+ }
+ break;
+ case R_PERIOD:
+ s->regs[R_PERIOD] = value;
+ ptimer_set_count(s->ptimer, value);
+ break;
+ case R_SNAPSHOT:
+ error_report("lm32_timer: write access to read only register 0x"
+ TARGET_FMT_plx, addr << 2);
+ break;
+ default:
+ error_report("lm32_timer: write access to unknown register 0x"
+ TARGET_FMT_plx, addr << 2);
+ break;
+ }
+ timer_update_irq(s);
+}
+
+static const MemoryRegionOps timer_ops = {
+ .read = timer_read,
+ .write = timer_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+};
+
+static void timer_hit(void *opaque)
+{
+ LM32TimerState *s = opaque;
+
+ trace_lm32_timer_hit();
+
+ s->regs[R_SR] |= SR_TO;
+
+ if (s->regs[R_CR] & CR_CONT) {
+ ptimer_set_count(s->ptimer, s->regs[R_PERIOD]);
+ ptimer_run(s->ptimer, 1);
+ }
+ timer_update_irq(s);
+}
+
+static void timer_reset(DeviceState *d)
+{
+ LM32TimerState *s = container_of(d, LM32TimerState, busdev.qdev);
+ int i;
+
+ for (i = 0; i < R_MAX; i++) {
+ s->regs[i] = 0;
+ }
+ ptimer_stop(s->ptimer);
+}
+
+static int lm32_timer_init(SysBusDevice *dev)
+{
+ LM32TimerState *s = FROM_SYSBUS(typeof(*s), dev);
+
+ sysbus_init_irq(dev, &s->irq);
+
+ s->bh = qemu_bh_new(timer_hit, s);
+ s->ptimer = ptimer_init(s->bh);
+ ptimer_set_freq(s->ptimer, s->freq_hz);
+
+ memory_region_init_io(&s->iomem, &timer_ops, s, "timer", R_MAX * 4);
+ sysbus_init_mmio(dev, &s->iomem);
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_lm32_timer = {
+ .name = "lm32-timer",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_PTIMER(ptimer, LM32TimerState),
+ VMSTATE_UINT32(freq_hz, LM32TimerState),
+ VMSTATE_UINT32_ARRAY(regs, LM32TimerState, R_MAX),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static Property lm32_timer_properties[] = {
+ DEFINE_PROP_UINT32("frequency", LM32TimerState, freq_hz, DEFAULT_FREQUENCY),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void lm32_timer_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = lm32_timer_init;
+ dc->reset = timer_reset;
+ dc->vmsd = &vmstate_lm32_timer;
+ dc->props = lm32_timer_properties;
+}
+
+static const TypeInfo lm32_timer_info = {
+ .name = "lm32-timer",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(LM32TimerState),
+ .class_init = lm32_timer_class_init,
+};
+
+static void lm32_timer_register_types(void)
+{
+ type_register_static(&lm32_timer_info);
+}
+
+type_init(lm32_timer_register_types)
--- /dev/null
+/*
+ * QEMU model of the Milkymist System Controller.
+ *
+ * Copyright (c) 2010-2012 Michael Walle <michael@walle.cc>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * Specification available at:
+ * http://www.milkymist.org/socdoc/sysctl.pdf
+ */
+
+#include "hw/hw.h"
+#include "hw/sysbus.h"
+#include "sysemu/sysemu.h"
+#include "trace.h"
+#include "qemu/timer.h"
+#include "hw/ptimer.h"
+#include "qemu/error-report.h"
+
+enum {
+ CTRL_ENABLE = (1<<0),
+ CTRL_AUTORESTART = (1<<1),
+};
+
+enum {
+ ICAP_READY = (1<<0),
+};
+
+enum {
+ R_GPIO_IN = 0,
+ R_GPIO_OUT,
+ R_GPIO_INTEN,
+ R_TIMER0_CONTROL = 4,
+ R_TIMER0_COMPARE,
+ R_TIMER0_COUNTER,
+ R_TIMER1_CONTROL = 8,
+ R_TIMER1_COMPARE,
+ R_TIMER1_COUNTER,
+ R_ICAP = 16,
+ R_DBG_SCRATCHPAD = 20,
+ R_DBG_WRITE_LOCK,
+ R_CLK_FREQUENCY = 29,
+ R_CAPABILITIES,
+ R_SYSTEM_ID,
+ R_MAX
+};
+
+struct MilkymistSysctlState {
+ SysBusDevice busdev;
+ MemoryRegion regs_region;
+
+ QEMUBH *bh0;
+ QEMUBH *bh1;
+ ptimer_state *ptimer0;
+ ptimer_state *ptimer1;
+
+ uint32_t freq_hz;
+ uint32_t capabilities;
+ uint32_t systemid;
+ uint32_t strappings;
+
+ uint32_t regs[R_MAX];
+
+ qemu_irq gpio_irq;
+ qemu_irq timer0_irq;
+ qemu_irq timer1_irq;
+};
+typedef struct MilkymistSysctlState MilkymistSysctlState;
+
+static void sysctl_icap_write(MilkymistSysctlState *s, uint32_t value)
+{
+ trace_milkymist_sysctl_icap_write(value);
+ switch (value & 0xffff) {
+ case 0x000e:
+ qemu_system_shutdown_request();
+ break;
+ }
+}
+
+static uint64_t sysctl_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ MilkymistSysctlState *s = opaque;
+ uint32_t r = 0;
+
+ addr >>= 2;
+ switch (addr) {
+ case R_TIMER0_COUNTER:
+ r = (uint32_t)ptimer_get_count(s->ptimer0);
+ /* milkymist timer counts up */
+ r = s->regs[R_TIMER0_COMPARE] - r;
+ break;
+ case R_TIMER1_COUNTER:
+ r = (uint32_t)ptimer_get_count(s->ptimer1);
+ /* milkymist timer counts up */
+ r = s->regs[R_TIMER1_COMPARE] - r;
+ break;
+ case R_GPIO_IN:
+ case R_GPIO_OUT:
+ case R_GPIO_INTEN:
+ case R_TIMER0_CONTROL:
+ case R_TIMER0_COMPARE:
+ case R_TIMER1_CONTROL:
+ case R_TIMER1_COMPARE:
+ case R_ICAP:
+ case R_DBG_SCRATCHPAD:
+ case R_DBG_WRITE_LOCK:
+ case R_CLK_FREQUENCY:
+ case R_CAPABILITIES:
+ case R_SYSTEM_ID:
+ r = s->regs[addr];
+ break;
+
+ default:
+ error_report("milkymist_sysctl: read access to unknown register 0x"
+ TARGET_FMT_plx, addr << 2);
+ break;
+ }
+
+ trace_milkymist_sysctl_memory_read(addr << 2, r);
+
+ return r;
+}
+
+static void sysctl_write(void *opaque, hwaddr addr, uint64_t value,
+ unsigned size)
+{
+ MilkymistSysctlState *s = opaque;
+
+ trace_milkymist_sysctl_memory_write(addr, value);
+
+ addr >>= 2;
+ switch (addr) {
+ case R_GPIO_OUT:
+ case R_GPIO_INTEN:
+ case R_TIMER0_COUNTER:
+ case R_TIMER1_COUNTER:
+ case R_DBG_SCRATCHPAD:
+ s->regs[addr] = value;
+ break;
+ case R_TIMER0_COMPARE:
+ ptimer_set_limit(s->ptimer0, value, 0);
+ s->regs[addr] = value;
+ break;
+ case R_TIMER1_COMPARE:
+ ptimer_set_limit(s->ptimer1, value, 0);
+ s->regs[addr] = value;
+ break;
+ case R_TIMER0_CONTROL:
+ s->regs[addr] = value;
+ if (s->regs[R_TIMER0_CONTROL] & CTRL_ENABLE) {
+ trace_milkymist_sysctl_start_timer0();
+ ptimer_set_count(s->ptimer0,
+ s->regs[R_TIMER0_COMPARE] - s->regs[R_TIMER0_COUNTER]);
+ ptimer_run(s->ptimer0, 0);
+ } else {
+ trace_milkymist_sysctl_stop_timer0();
+ ptimer_stop(s->ptimer0);
+ }
+ break;
+ case R_TIMER1_CONTROL:
+ s->regs[addr] = value;
+ if (s->regs[R_TIMER1_CONTROL] & CTRL_ENABLE) {
+ trace_milkymist_sysctl_start_timer1();
+ ptimer_set_count(s->ptimer1,
+ s->regs[R_TIMER1_COMPARE] - s->regs[R_TIMER1_COUNTER]);
+ ptimer_run(s->ptimer1, 0);
+ } else {
+ trace_milkymist_sysctl_stop_timer1();
+ ptimer_stop(s->ptimer1);
+ }
+ break;
+ case R_ICAP:
+ sysctl_icap_write(s, value);
+ break;
+ case R_DBG_WRITE_LOCK:
+ s->regs[addr] = 1;
+ break;
+ case R_SYSTEM_ID:
+ qemu_system_reset_request();
+ break;
+
+ case R_GPIO_IN:
+ case R_CLK_FREQUENCY:
+ case R_CAPABILITIES:
+ error_report("milkymist_sysctl: write to read-only register 0x"
+ TARGET_FMT_plx, addr << 2);
+ break;
+
+ default:
+ error_report("milkymist_sysctl: write access to unknown register 0x"
+ TARGET_FMT_plx, addr << 2);
+ break;
+ }
+}
+
+static const MemoryRegionOps sysctl_mmio_ops = {
+ .read = sysctl_read,
+ .write = sysctl_write,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void timer0_hit(void *opaque)
+{
+ MilkymistSysctlState *s = opaque;
+
+ if (!(s->regs[R_TIMER0_CONTROL] & CTRL_AUTORESTART)) {
+ s->regs[R_TIMER0_CONTROL] &= ~CTRL_ENABLE;
+ trace_milkymist_sysctl_stop_timer0();
+ ptimer_stop(s->ptimer0);
+ }
+
+ trace_milkymist_sysctl_pulse_irq_timer0();
+ qemu_irq_pulse(s->timer0_irq);
+}
+
+static void timer1_hit(void *opaque)
+{
+ MilkymistSysctlState *s = opaque;
+
+ if (!(s->regs[R_TIMER1_CONTROL] & CTRL_AUTORESTART)) {
+ s->regs[R_TIMER1_CONTROL] &= ~CTRL_ENABLE;
+ trace_milkymist_sysctl_stop_timer1();
+ ptimer_stop(s->ptimer1);
+ }
+
+ trace_milkymist_sysctl_pulse_irq_timer1();
+ qemu_irq_pulse(s->timer1_irq);
+}
+
+static void milkymist_sysctl_reset(DeviceState *d)
+{
+ MilkymistSysctlState *s =
+ container_of(d, MilkymistSysctlState, busdev.qdev);
+ int i;
+
+ for (i = 0; i < R_MAX; i++) {
+ s->regs[i] = 0;
+ }
+
+ ptimer_stop(s->ptimer0);
+ ptimer_stop(s->ptimer1);
+
+ /* defaults */
+ s->regs[R_ICAP] = ICAP_READY;
+ s->regs[R_SYSTEM_ID] = s->systemid;
+ s->regs[R_CLK_FREQUENCY] = s->freq_hz;
+ s->regs[R_CAPABILITIES] = s->capabilities;
+ s->regs[R_GPIO_IN] = s->strappings;
+}
+
+static int milkymist_sysctl_init(SysBusDevice *dev)
+{
+ MilkymistSysctlState *s = FROM_SYSBUS(typeof(*s), dev);
+
+ sysbus_init_irq(dev, &s->gpio_irq);
+ sysbus_init_irq(dev, &s->timer0_irq);
+ sysbus_init_irq(dev, &s->timer1_irq);
+
+ s->bh0 = qemu_bh_new(timer0_hit, s);
+ s->bh1 = qemu_bh_new(timer1_hit, s);
+ s->ptimer0 = ptimer_init(s->bh0);
+ s->ptimer1 = ptimer_init(s->bh1);
+ ptimer_set_freq(s->ptimer0, s->freq_hz);
+ ptimer_set_freq(s->ptimer1, s->freq_hz);
+
+ memory_region_init_io(&s->regs_region, &sysctl_mmio_ops, s,
+ "milkymist-sysctl", R_MAX * 4);
+ sysbus_init_mmio(dev, &s->regs_region);
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_milkymist_sysctl = {
+ .name = "milkymist-sysctl",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(regs, MilkymistSysctlState, R_MAX),
+ VMSTATE_PTIMER(ptimer0, MilkymistSysctlState),
+ VMSTATE_PTIMER(ptimer1, MilkymistSysctlState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static Property milkymist_sysctl_properties[] = {
+ DEFINE_PROP_UINT32("frequency", MilkymistSysctlState,
+ freq_hz, 80000000),
+ DEFINE_PROP_UINT32("capabilities", MilkymistSysctlState,
+ capabilities, 0x00000000),
+ DEFINE_PROP_UINT32("systemid", MilkymistSysctlState,
+ systemid, 0x10014d31),
+ DEFINE_PROP_UINT32("gpio_strappings", MilkymistSysctlState,
+ strappings, 0x00000001),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void milkymist_sysctl_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = milkymist_sysctl_init;
+ dc->reset = milkymist_sysctl_reset;
+ dc->vmsd = &vmstate_milkymist_sysctl;
+ dc->props = milkymist_sysctl_properties;
+}
+
+static const TypeInfo milkymist_sysctl_info = {
+ .name = "milkymist-sysctl",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(MilkymistSysctlState),
+ .class_init = milkymist_sysctl_class_init,
+};
+
+static void milkymist_sysctl_register_types(void)
+{
+ type_register_static(&milkymist_sysctl_info);
+}
+
+type_init(milkymist_sysctl_register_types)
--- /dev/null
+/*
+ * 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/hw.h"
+#include "qemu/timer.h"
+#include "hw/arm/omap.h"
+
+/* GP timers */
+struct omap_gp_timer_s {
+ MemoryRegion iomem;
+ 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, hwaddr 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, hwaddr 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 void omap_gp_timer_write(void *opaque, hwaddr 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, hwaddr 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 const MemoryRegionOps omap_gp_timer_ops = {
+ .old_mmio = {
+ .read = {
+ omap_badwidth_read32,
+ omap_gp_timer_readh,
+ omap_gp_timer_readw,
+ },
+ .write = {
+ omap_badwidth_write32,
+ omap_gp_timer_writeh,
+ omap_gp_timer_write,
+ },
+ },
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+struct omap_gp_timer_s *omap_gp_timer_init(struct omap_target_agent_s *ta,
+ qemu_irq irq, omap_clk fclk, omap_clk iclk)
+{
+ struct omap_gp_timer_s *s = (struct omap_gp_timer_s *)
+ g_malloc0(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);
+
+ memory_region_init_io(&s->iomem, &omap_gp_timer_ops, s, "omap.gptimer",
+ omap_l4_region_size(ta, 0));
+ omap_l4_attach(ta, 0, &s->iomem);
+
+ return s;
+}
--- /dev/null
+/*
+ * TI OMAP2 32kHz sync timer 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/hw.h"
+#include "qemu/timer.h"
+#include "hw/arm/omap.h"
+struct omap_synctimer_s {
+ MemoryRegion iomem;
+ uint32_t val;
+ uint16_t readh;
+};
+
+/* 32-kHz Sync Timer of the OMAP2 */
+static uint32_t omap_synctimer_read(struct omap_synctimer_s *s) {
+ return muldiv64(qemu_get_clock_ns(vm_clock), 0x8000, get_ticks_per_sec());
+}
+
+void omap_synctimer_reset(struct omap_synctimer_s *s)
+{
+ s->val = omap_synctimer_read(s);
+}
+
+static uint32_t omap_synctimer_readw(void *opaque, hwaddr addr)
+{
+ struct omap_synctimer_s *s = (struct omap_synctimer_s *) opaque;
+
+ switch (addr) {
+ case 0x00: /* 32KSYNCNT_REV */
+ return 0x21;
+
+ case 0x10: /* CR */
+ return omap_synctimer_read(s) - s->val;
+ }
+
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static uint32_t omap_synctimer_readh(void *opaque, hwaddr addr)
+{
+ struct omap_synctimer_s *s = (struct omap_synctimer_s *) opaque;
+ uint32_t ret;
+
+ if (addr & 2)
+ return s->readh;
+ else {
+ ret = omap_synctimer_readw(opaque, addr);
+ s->readh = ret >> 16;
+ return ret & 0xffff;
+ }
+}
+
+static void omap_synctimer_write(void *opaque, hwaddr addr,
+ uint32_t value)
+{
+ OMAP_BAD_REG(addr);
+}
+
+static const MemoryRegionOps omap_synctimer_ops = {
+ .old_mmio = {
+ .read = {
+ omap_badwidth_read32,
+ omap_synctimer_readh,
+ omap_synctimer_readw,
+ },
+ .write = {
+ omap_badwidth_write32,
+ omap_synctimer_write,
+ omap_synctimer_write,
+ },
+ },
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+struct omap_synctimer_s *omap_synctimer_init(struct omap_target_agent_s *ta,
+ struct omap_mpu_state_s *mpu, omap_clk fclk, omap_clk iclk)
+{
+ struct omap_synctimer_s *s = g_malloc0(sizeof(*s));
+
+ omap_synctimer_reset(s);
+ memory_region_init_io(&s->iomem, &omap_synctimer_ops, s, "omap.synctimer",
+ omap_l4_region_size(ta, 0));
+ omap_l4_attach(ta, 0, &s->iomem);
+
+ return s;
+}
--- /dev/null
+/*
+ * Intel XScale PXA255/270 OS Timers.
+ *
+ * Copyright (c) 2006 Openedhand Ltd.
+ * Copyright (c) 2006 Thorsten Zitterell
+ *
+ * This code is licensed under the GPL.
+ */
+
+#include "hw/hw.h"
+#include "qemu/timer.h"
+#include "sysemu/sysemu.h"
+#include "hw/arm/pxa.h"
+#include "hw/sysbus.h"
+
+#define OSMR0 0x00
+#define OSMR1 0x04
+#define OSMR2 0x08
+#define OSMR3 0x0c
+#define OSMR4 0x80
+#define OSMR5 0x84
+#define OSMR6 0x88
+#define OSMR7 0x8c
+#define OSMR8 0x90
+#define OSMR9 0x94
+#define OSMR10 0x98
+#define OSMR11 0x9c
+#define OSCR 0x10 /* OS Timer Count */
+#define OSCR4 0x40
+#define OSCR5 0x44
+#define OSCR6 0x48
+#define OSCR7 0x4c
+#define OSCR8 0x50
+#define OSCR9 0x54
+#define OSCR10 0x58
+#define OSCR11 0x5c
+#define OSSR 0x14 /* Timer status register */
+#define OWER 0x18
+#define OIER 0x1c /* Interrupt enable register 3-0 to E3-E0 */
+#define OMCR4 0xc0 /* OS Match Control registers */
+#define OMCR5 0xc4
+#define OMCR6 0xc8
+#define OMCR7 0xcc
+#define OMCR8 0xd0
+#define OMCR9 0xd4
+#define OMCR10 0xd8
+#define OMCR11 0xdc
+#define OSNR 0x20
+
+#define PXA25X_FREQ 3686400 /* 3.6864 MHz */
+#define PXA27X_FREQ 3250000 /* 3.25 MHz */
+
+static int pxa2xx_timer4_freq[8] = {
+ [0] = 0,
+ [1] = 32768,
+ [2] = 1000,
+ [3] = 1,
+ [4] = 1000000,
+ /* [5] is the "Externally supplied clock". Assign if necessary. */
+ [5 ... 7] = 0,
+};
+
+typedef struct PXA2xxTimerInfo PXA2xxTimerInfo;
+
+typedef struct {
+ uint32_t value;
+ qemu_irq irq;
+ QEMUTimer *qtimer;
+ int num;
+ PXA2xxTimerInfo *info;
+} PXA2xxTimer0;
+
+typedef struct {
+ PXA2xxTimer0 tm;
+ int32_t oldclock;
+ int32_t clock;
+ uint64_t lastload;
+ uint32_t freq;
+ uint32_t control;
+} PXA2xxTimer4;
+
+struct PXA2xxTimerInfo {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+ uint32_t flags;
+
+ int32_t clock;
+ int32_t oldclock;
+ uint64_t lastload;
+ uint32_t freq;
+ PXA2xxTimer0 timer[4];
+ uint32_t events;
+ uint32_t irq_enabled;
+ uint32_t reset3;
+ uint32_t snapshot;
+
+ qemu_irq irq4;
+ PXA2xxTimer4 tm4[8];
+};
+
+#define PXA2XX_TIMER_HAVE_TM4 0
+
+static inline int pxa2xx_timer_has_tm4(PXA2xxTimerInfo *s)
+{
+ return s->flags & (1 << PXA2XX_TIMER_HAVE_TM4);
+}
+
+static void pxa2xx_timer_update(void *opaque, uint64_t now_qemu)
+{
+ PXA2xxTimerInfo *s = (PXA2xxTimerInfo *) opaque;
+ int i;
+ uint32_t now_vm;
+ uint64_t new_qemu;
+
+ now_vm = s->clock +
+ muldiv64(now_qemu - s->lastload, s->freq, get_ticks_per_sec());
+
+ for (i = 0; i < 4; i ++) {
+ new_qemu = now_qemu + muldiv64((uint32_t) (s->timer[i].value - now_vm),
+ get_ticks_per_sec(), s->freq);
+ qemu_mod_timer(s->timer[i].qtimer, new_qemu);
+ }
+}
+
+static void pxa2xx_timer_update4(void *opaque, uint64_t now_qemu, int n)
+{
+ PXA2xxTimerInfo *s = (PXA2xxTimerInfo *) opaque;
+ uint32_t now_vm;
+ uint64_t new_qemu;
+ static const int counters[8] = { 0, 0, 0, 0, 4, 4, 6, 6 };
+ int counter;
+
+ if (s->tm4[n].control & (1 << 7))
+ counter = n;
+ else
+ counter = counters[n];
+
+ if (!s->tm4[counter].freq) {
+ qemu_del_timer(s->tm4[n].tm.qtimer);
+ return;
+ }
+
+ now_vm = s->tm4[counter].clock + muldiv64(now_qemu -
+ s->tm4[counter].lastload,
+ s->tm4[counter].freq, get_ticks_per_sec());
+
+ new_qemu = now_qemu + muldiv64((uint32_t) (s->tm4[n].tm.value - now_vm),
+ get_ticks_per_sec(), s->tm4[counter].freq);
+ qemu_mod_timer(s->tm4[n].tm.qtimer, new_qemu);
+}
+
+static uint64_t pxa2xx_timer_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ PXA2xxTimerInfo *s = (PXA2xxTimerInfo *) opaque;
+ int tm = 0;
+
+ switch (offset) {
+ case OSMR3: tm ++;
+ /* fall through */
+ case OSMR2: tm ++;
+ /* fall through */
+ case OSMR1: tm ++;
+ /* fall through */
+ case OSMR0:
+ return s->timer[tm].value;
+ case OSMR11: tm ++;
+ /* fall through */
+ case OSMR10: tm ++;
+ /* fall through */
+ case OSMR9: tm ++;
+ /* fall through */
+ case OSMR8: tm ++;
+ /* fall through */
+ case OSMR7: tm ++;
+ /* fall through */
+ case OSMR6: tm ++;
+ /* fall through */
+ case OSMR5: tm ++;
+ /* fall through */
+ case OSMR4:
+ if (!pxa2xx_timer_has_tm4(s))
+ goto badreg;
+ return s->tm4[tm].tm.value;
+ case OSCR:
+ return s->clock + muldiv64(qemu_get_clock_ns(vm_clock) -
+ s->lastload, s->freq, get_ticks_per_sec());
+ case OSCR11: tm ++;
+ /* fall through */
+ case OSCR10: tm ++;
+ /* fall through */
+ case OSCR9: tm ++;
+ /* fall through */
+ case OSCR8: tm ++;
+ /* fall through */
+ case OSCR7: tm ++;
+ /* fall through */
+ case OSCR6: tm ++;
+ /* fall through */
+ case OSCR5: tm ++;
+ /* fall through */
+ case OSCR4:
+ if (!pxa2xx_timer_has_tm4(s))
+ goto badreg;
+
+ if ((tm == 9 - 4 || tm == 11 - 4) && (s->tm4[tm].control & (1 << 9))) {
+ if (s->tm4[tm - 1].freq)
+ s->snapshot = s->tm4[tm - 1].clock + muldiv64(
+ qemu_get_clock_ns(vm_clock) -
+ s->tm4[tm - 1].lastload,
+ s->tm4[tm - 1].freq, get_ticks_per_sec());
+ else
+ s->snapshot = s->tm4[tm - 1].clock;
+ }
+
+ if (!s->tm4[tm].freq)
+ return s->tm4[tm].clock;
+ return s->tm4[tm].clock + muldiv64(qemu_get_clock_ns(vm_clock) -
+ s->tm4[tm].lastload, s->tm4[tm].freq, get_ticks_per_sec());
+ case OIER:
+ return s->irq_enabled;
+ case OSSR: /* Status register */
+ return s->events;
+ case OWER:
+ return s->reset3;
+ case OMCR11: tm ++;
+ /* fall through */
+ case OMCR10: tm ++;
+ /* fall through */
+ case OMCR9: tm ++;
+ /* fall through */
+ case OMCR8: tm ++;
+ /* fall through */
+ case OMCR7: tm ++;
+ /* fall through */
+ case OMCR6: tm ++;
+ /* fall through */
+ case OMCR5: tm ++;
+ /* fall through */
+ case OMCR4:
+ if (!pxa2xx_timer_has_tm4(s))
+ goto badreg;
+ return s->tm4[tm].control;
+ case OSNR:
+ return s->snapshot;
+ default:
+ badreg:
+ hw_error("pxa2xx_timer_read: Bad offset " REG_FMT "\n", offset);
+ }
+
+ return 0;
+}
+
+static void pxa2xx_timer_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ int i, tm = 0;
+ PXA2xxTimerInfo *s = (PXA2xxTimerInfo *) opaque;
+
+ switch (offset) {
+ case OSMR3: tm ++;
+ /* fall through */
+ case OSMR2: tm ++;
+ /* fall through */
+ case OSMR1: tm ++;
+ /* fall through */
+ case OSMR0:
+ s->timer[tm].value = value;
+ pxa2xx_timer_update(s, qemu_get_clock_ns(vm_clock));
+ break;
+ case OSMR11: tm ++;
+ /* fall through */
+ case OSMR10: tm ++;
+ /* fall through */
+ case OSMR9: tm ++;
+ /* fall through */
+ case OSMR8: tm ++;
+ /* fall through */
+ case OSMR7: tm ++;
+ /* fall through */
+ case OSMR6: tm ++;
+ /* fall through */
+ case OSMR5: tm ++;
+ /* fall through */
+ case OSMR4:
+ if (!pxa2xx_timer_has_tm4(s))
+ goto badreg;
+ s->tm4[tm].tm.value = value;
+ pxa2xx_timer_update4(s, qemu_get_clock_ns(vm_clock), tm);
+ break;
+ case OSCR:
+ s->oldclock = s->clock;
+ s->lastload = qemu_get_clock_ns(vm_clock);
+ s->clock = value;
+ pxa2xx_timer_update(s, s->lastload);
+ break;
+ case OSCR11: tm ++;
+ /* fall through */
+ case OSCR10: tm ++;
+ /* fall through */
+ case OSCR9: tm ++;
+ /* fall through */
+ case OSCR8: tm ++;
+ /* fall through */
+ case OSCR7: tm ++;
+ /* fall through */
+ case OSCR6: tm ++;
+ /* fall through */
+ case OSCR5: tm ++;
+ /* fall through */
+ case OSCR4:
+ if (!pxa2xx_timer_has_tm4(s))
+ goto badreg;
+ s->tm4[tm].oldclock = s->tm4[tm].clock;
+ s->tm4[tm].lastload = qemu_get_clock_ns(vm_clock);
+ s->tm4[tm].clock = value;
+ pxa2xx_timer_update4(s, s->tm4[tm].lastload, tm);
+ break;
+ case OIER:
+ s->irq_enabled = value & 0xfff;
+ break;
+ case OSSR: /* Status register */
+ value &= s->events;
+ s->events &= ~value;
+ for (i = 0; i < 4; i ++, value >>= 1)
+ if (value & 1)
+ qemu_irq_lower(s->timer[i].irq);
+ if (pxa2xx_timer_has_tm4(s) && !(s->events & 0xff0) && value)
+ qemu_irq_lower(s->irq4);
+ break;
+ case OWER: /* XXX: Reset on OSMR3 match? */
+ s->reset3 = value;
+ break;
+ case OMCR7: tm ++;
+ /* fall through */
+ case OMCR6: tm ++;
+ /* fall through */
+ case OMCR5: tm ++;
+ /* fall through */
+ case OMCR4:
+ if (!pxa2xx_timer_has_tm4(s))
+ goto badreg;
+ s->tm4[tm].control = value & 0x0ff;
+ /* XXX Stop if running (shouldn't happen) */
+ if ((value & (1 << 7)) || tm == 0)
+ s->tm4[tm].freq = pxa2xx_timer4_freq[value & 7];
+ else {
+ s->tm4[tm].freq = 0;
+ pxa2xx_timer_update4(s, qemu_get_clock_ns(vm_clock), tm);
+ }
+ break;
+ case OMCR11: tm ++;
+ /* fall through */
+ case OMCR10: tm ++;
+ /* fall through */
+ case OMCR9: tm ++;
+ /* fall through */
+ case OMCR8: tm += 4;
+ if (!pxa2xx_timer_has_tm4(s))
+ goto badreg;
+ s->tm4[tm].control = value & 0x3ff;
+ /* XXX Stop if running (shouldn't happen) */
+ if ((value & (1 << 7)) || !(tm & 1))
+ s->tm4[tm].freq =
+ pxa2xx_timer4_freq[(value & (1 << 8)) ? 0 : (value & 7)];
+ else {
+ s->tm4[tm].freq = 0;
+ pxa2xx_timer_update4(s, qemu_get_clock_ns(vm_clock), tm);
+ }
+ break;
+ default:
+ badreg:
+ hw_error("pxa2xx_timer_write: Bad offset " REG_FMT "\n", offset);
+ }
+}
+
+static const MemoryRegionOps pxa2xx_timer_ops = {
+ .read = pxa2xx_timer_read,
+ .write = pxa2xx_timer_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void pxa2xx_timer_tick(void *opaque)
+{
+ PXA2xxTimer0 *t = (PXA2xxTimer0 *) opaque;
+ PXA2xxTimerInfo *i = t->info;
+
+ if (i->irq_enabled & (1 << t->num)) {
+ i->events |= 1 << t->num;
+ qemu_irq_raise(t->irq);
+ }
+
+ if (t->num == 3)
+ if (i->reset3 & 1) {
+ i->reset3 = 0;
+ qemu_system_reset_request();
+ }
+}
+
+static void pxa2xx_timer_tick4(void *opaque)
+{
+ PXA2xxTimer4 *t = (PXA2xxTimer4 *) opaque;
+ PXA2xxTimerInfo *i = (PXA2xxTimerInfo *) t->tm.info;
+
+ pxa2xx_timer_tick(&t->tm);
+ if (t->control & (1 << 3))
+ t->clock = 0;
+ if (t->control & (1 << 6))
+ pxa2xx_timer_update4(i, qemu_get_clock_ns(vm_clock), t->tm.num - 4);
+ if (i->events & 0xff0)
+ qemu_irq_raise(i->irq4);
+}
+
+static int pxa25x_timer_post_load(void *opaque, int version_id)
+{
+ PXA2xxTimerInfo *s = (PXA2xxTimerInfo *) opaque;
+ int64_t now;
+ int i;
+
+ now = qemu_get_clock_ns(vm_clock);
+ pxa2xx_timer_update(s, now);
+
+ if (pxa2xx_timer_has_tm4(s))
+ for (i = 0; i < 8; i ++)
+ pxa2xx_timer_update4(s, now, i);
+
+ return 0;
+}
+
+static int pxa2xx_timer_init(SysBusDevice *dev)
+{
+ int i;
+ PXA2xxTimerInfo *s;
+
+ s = FROM_SYSBUS(PXA2xxTimerInfo, dev);
+ s->irq_enabled = 0;
+ s->oldclock = 0;
+ s->clock = 0;
+ s->lastload = qemu_get_clock_ns(vm_clock);
+ s->reset3 = 0;
+
+ for (i = 0; i < 4; i ++) {
+ s->timer[i].value = 0;
+ sysbus_init_irq(dev, &s->timer[i].irq);
+ s->timer[i].info = s;
+ s->timer[i].num = i;
+ s->timer[i].qtimer = qemu_new_timer_ns(vm_clock,
+ pxa2xx_timer_tick, &s->timer[i]);
+ }
+ if (s->flags & (1 << PXA2XX_TIMER_HAVE_TM4)) {
+ sysbus_init_irq(dev, &s->irq4);
+
+ for (i = 0; i < 8; i ++) {
+ s->tm4[i].tm.value = 0;
+ s->tm4[i].tm.info = s;
+ s->tm4[i].tm.num = i + 4;
+ s->tm4[i].freq = 0;
+ s->tm4[i].control = 0x0;
+ s->tm4[i].tm.qtimer = qemu_new_timer_ns(vm_clock,
+ pxa2xx_timer_tick4, &s->tm4[i]);
+ }
+ }
+
+ memory_region_init_io(&s->iomem, &pxa2xx_timer_ops, s,
+ "pxa2xx-timer", 0x00001000);
+ sysbus_init_mmio(dev, &s->iomem);
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_pxa2xx_timer0_regs = {
+ .name = "pxa2xx_timer0",
+ .version_id = 2,
+ .minimum_version_id = 2,
+ .minimum_version_id_old = 2,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(value, PXA2xxTimer0),
+ VMSTATE_END_OF_LIST(),
+ },
+};
+
+static const VMStateDescription vmstate_pxa2xx_timer4_regs = {
+ .name = "pxa2xx_timer4",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_STRUCT(tm, PXA2xxTimer4, 1,
+ vmstate_pxa2xx_timer0_regs, PXA2xxTimer0),
+ VMSTATE_INT32(oldclock, PXA2xxTimer4),
+ VMSTATE_INT32(clock, PXA2xxTimer4),
+ VMSTATE_UINT64(lastload, PXA2xxTimer4),
+ VMSTATE_UINT32(freq, PXA2xxTimer4),
+ VMSTATE_UINT32(control, PXA2xxTimer4),
+ VMSTATE_END_OF_LIST(),
+ },
+};
+
+static bool pxa2xx_timer_has_tm4_test(void *opaque, int version_id)
+{
+ return pxa2xx_timer_has_tm4(opaque);
+}
+
+static const VMStateDescription vmstate_pxa2xx_timer_regs = {
+ .name = "pxa2xx_timer",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .post_load = pxa25x_timer_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_INT32(clock, PXA2xxTimerInfo),
+ VMSTATE_INT32(oldclock, PXA2xxTimerInfo),
+ VMSTATE_UINT64(lastload, PXA2xxTimerInfo),
+ VMSTATE_STRUCT_ARRAY(timer, PXA2xxTimerInfo, 4, 1,
+ vmstate_pxa2xx_timer0_regs, PXA2xxTimer0),
+ VMSTATE_UINT32(events, PXA2xxTimerInfo),
+ VMSTATE_UINT32(irq_enabled, PXA2xxTimerInfo),
+ VMSTATE_UINT32(reset3, PXA2xxTimerInfo),
+ VMSTATE_UINT32(snapshot, PXA2xxTimerInfo),
+ VMSTATE_STRUCT_ARRAY_TEST(tm4, PXA2xxTimerInfo, 8,
+ pxa2xx_timer_has_tm4_test, 0,
+ vmstate_pxa2xx_timer4_regs, PXA2xxTimer4),
+ VMSTATE_END_OF_LIST(),
+ }
+};
+
+static Property pxa25x_timer_dev_properties[] = {
+ DEFINE_PROP_UINT32("freq", PXA2xxTimerInfo, freq, PXA25X_FREQ),
+ DEFINE_PROP_BIT("tm4", PXA2xxTimerInfo, flags,
+ PXA2XX_TIMER_HAVE_TM4, false),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void pxa25x_timer_dev_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = pxa2xx_timer_init;
+ dc->desc = "PXA25x timer";
+ dc->vmsd = &vmstate_pxa2xx_timer_regs;
+ dc->props = pxa25x_timer_dev_properties;
+}
+
+static const TypeInfo pxa25x_timer_dev_info = {
+ .name = "pxa25x-timer",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(PXA2xxTimerInfo),
+ .class_init = pxa25x_timer_dev_class_init,
+};
+
+static Property pxa27x_timer_dev_properties[] = {
+ DEFINE_PROP_UINT32("freq", PXA2xxTimerInfo, freq, PXA27X_FREQ),
+ DEFINE_PROP_BIT("tm4", PXA2xxTimerInfo, flags,
+ PXA2XX_TIMER_HAVE_TM4, true),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void pxa27x_timer_dev_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = pxa2xx_timer_init;
+ dc->desc = "PXA27x timer";
+ dc->vmsd = &vmstate_pxa2xx_timer_regs;
+ dc->props = pxa27x_timer_dev_properties;
+}
+
+static const TypeInfo pxa27x_timer_dev_info = {
+ .name = "pxa27x-timer",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(PXA2xxTimerInfo),
+ .class_init = pxa27x_timer_dev_class_init,
+};
+
+static void pxa2xx_timer_register_types(void)
+{
+ type_register_static(&pxa25x_timer_dev_info);
+ type_register_static(&pxa27x_timer_dev_info);
+}
+
+type_init(pxa2xx_timer_register_types)
--- /dev/null
+/*
+ * SuperH Timer modules.
+ *
+ * Copyright (c) 2007 Magnus Damm
+ * Based on arm_timer.c by Paul Brook
+ * Copyright (c) 2005-2006 CodeSourcery.
+ *
+ * This code is licensed under the GPL.
+ */
+
+#include "hw/hw.h"
+#include "hw/sh4/sh.h"
+#include "qemu/timer.h"
+#include "exec/address-spaces.h"
+#include "hw/ptimer.h"
+
+//#define DEBUG_TIMER
+
+#define TIMER_TCR_TPSC (7 << 0)
+#define TIMER_TCR_CKEG (3 << 3)
+#define TIMER_TCR_UNIE (1 << 5)
+#define TIMER_TCR_ICPE (3 << 6)
+#define TIMER_TCR_UNF (1 << 8)
+#define TIMER_TCR_ICPF (1 << 9)
+#define TIMER_TCR_RESERVED (0x3f << 10)
+
+#define TIMER_FEAT_CAPT (1 << 0)
+#define TIMER_FEAT_EXTCLK (1 << 1)
+
+#define OFFSET_TCOR 0
+#define OFFSET_TCNT 1
+#define OFFSET_TCR 2
+#define OFFSET_TCPR 3
+
+typedef struct {
+ ptimer_state *timer;
+ uint32_t tcnt;
+ uint32_t tcor;
+ uint32_t tcr;
+ uint32_t tcpr;
+ int freq;
+ int int_level;
+ int old_level;
+ int feat;
+ int enabled;
+ qemu_irq irq;
+} sh_timer_state;
+
+/* Check all active timers, and schedule the next timer interrupt. */
+
+static void sh_timer_update(sh_timer_state *s)
+{
+ int new_level = s->int_level && (s->tcr & TIMER_TCR_UNIE);
+
+ if (new_level != s->old_level)
+ qemu_set_irq (s->irq, new_level);
+
+ s->old_level = s->int_level;
+ s->int_level = new_level;
+}
+
+static uint32_t sh_timer_read(void *opaque, hwaddr offset)
+{
+ sh_timer_state *s = (sh_timer_state *)opaque;
+
+ switch (offset >> 2) {
+ case OFFSET_TCOR:
+ return s->tcor;
+ case OFFSET_TCNT:
+ return ptimer_get_count(s->timer);
+ case OFFSET_TCR:
+ return s->tcr | (s->int_level ? TIMER_TCR_UNF : 0);
+ case OFFSET_TCPR:
+ if (s->feat & TIMER_FEAT_CAPT)
+ return s->tcpr;
+ default:
+ hw_error("sh_timer_read: Bad offset %x\n", (int)offset);
+ return 0;
+ }
+}
+
+static void sh_timer_write(void *opaque, hwaddr offset,
+ uint32_t value)
+{
+ sh_timer_state *s = (sh_timer_state *)opaque;
+ int freq;
+
+ switch (offset >> 2) {
+ case OFFSET_TCOR:
+ s->tcor = value;
+ ptimer_set_limit(s->timer, s->tcor, 0);
+ break;
+ case OFFSET_TCNT:
+ s->tcnt = value;
+ ptimer_set_count(s->timer, s->tcnt);
+ break;
+ case OFFSET_TCR:
+ if (s->enabled) {
+ /* Pause the timer if it is running. This may cause some
+ inaccuracy dure to rounding, but avoids a whole lot of other
+ messyness. */
+ ptimer_stop(s->timer);
+ }
+ freq = s->freq;
+ /* ??? Need to recalculate expiry time after changing divisor. */
+ switch (value & TIMER_TCR_TPSC) {
+ case 0: freq >>= 2; break;
+ case 1: freq >>= 4; break;
+ case 2: freq >>= 6; break;
+ case 3: freq >>= 8; break;
+ case 4: freq >>= 10; break;
+ case 6:
+ case 7: if (s->feat & TIMER_FEAT_EXTCLK) break;
+ default: hw_error("sh_timer_write: Reserved TPSC value\n"); break;
+ }
+ switch ((value & TIMER_TCR_CKEG) >> 3) {
+ case 0: break;
+ case 1:
+ case 2:
+ case 3: if (s->feat & TIMER_FEAT_EXTCLK) break;
+ default: hw_error("sh_timer_write: Reserved CKEG value\n"); break;
+ }
+ switch ((value & TIMER_TCR_ICPE) >> 6) {
+ case 0: break;
+ case 2:
+ case 3: if (s->feat & TIMER_FEAT_CAPT) break;
+ default: hw_error("sh_timer_write: Reserved ICPE value\n"); break;
+ }
+ if ((value & TIMER_TCR_UNF) == 0)
+ s->int_level = 0;
+
+ value &= ~TIMER_TCR_UNF;
+
+ if ((value & TIMER_TCR_ICPF) && (!(s->feat & TIMER_FEAT_CAPT)))
+ hw_error("sh_timer_write: Reserved ICPF value\n");
+
+ value &= ~TIMER_TCR_ICPF; /* capture not supported */
+
+ if (value & TIMER_TCR_RESERVED)
+ hw_error("sh_timer_write: Reserved TCR bits set\n");
+ s->tcr = value;
+ ptimer_set_limit(s->timer, s->tcor, 0);
+ ptimer_set_freq(s->timer, freq);
+ if (s->enabled) {
+ /* Restart the timer if still enabled. */
+ ptimer_run(s->timer, 0);
+ }
+ break;
+ case OFFSET_TCPR:
+ if (s->feat & TIMER_FEAT_CAPT) {
+ s->tcpr = value;
+ break;
+ }
+ default:
+ hw_error("sh_timer_write: Bad offset %x\n", (int)offset);
+ }
+ sh_timer_update(s);
+}
+
+static void sh_timer_start_stop(void *opaque, int enable)
+{
+ sh_timer_state *s = (sh_timer_state *)opaque;
+
+#ifdef DEBUG_TIMER
+ printf("sh_timer_start_stop %d (%d)\n", enable, s->enabled);
+#endif
+
+ if (s->enabled && !enable) {
+ ptimer_stop(s->timer);
+ }
+ if (!s->enabled && enable) {
+ ptimer_run(s->timer, 0);
+ }
+ s->enabled = !!enable;
+
+#ifdef DEBUG_TIMER
+ printf("sh_timer_start_stop done %d\n", s->enabled);
+#endif
+}
+
+static void sh_timer_tick(void *opaque)
+{
+ sh_timer_state *s = (sh_timer_state *)opaque;
+ s->int_level = s->enabled;
+ sh_timer_update(s);
+}
+
+static void *sh_timer_init(uint32_t freq, int feat, qemu_irq irq)
+{
+ sh_timer_state *s;
+ QEMUBH *bh;
+
+ s = (sh_timer_state *)g_malloc0(sizeof(sh_timer_state));
+ s->freq = freq;
+ s->feat = feat;
+ s->tcor = 0xffffffff;
+ s->tcnt = 0xffffffff;
+ s->tcpr = 0xdeadbeef;
+ s->tcr = 0;
+ s->enabled = 0;
+ s->irq = irq;
+
+ bh = qemu_bh_new(sh_timer_tick, s);
+ s->timer = ptimer_init(bh);
+
+ sh_timer_write(s, OFFSET_TCOR >> 2, s->tcor);
+ sh_timer_write(s, OFFSET_TCNT >> 2, s->tcnt);
+ sh_timer_write(s, OFFSET_TCPR >> 2, s->tcpr);
+ sh_timer_write(s, OFFSET_TCR >> 2, s->tcpr);
+ /* ??? Save/restore. */
+ return s;
+}
+
+typedef struct {
+ MemoryRegion iomem;
+ MemoryRegion iomem_p4;
+ MemoryRegion iomem_a7;
+ void *timer[3];
+ int level[3];
+ uint32_t tocr;
+ uint32_t tstr;
+ int feat;
+} tmu012_state;
+
+static uint64_t tmu012_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ tmu012_state *s = (tmu012_state *)opaque;
+
+#ifdef DEBUG_TIMER
+ printf("tmu012_read 0x%lx\n", (unsigned long) offset);
+#endif
+
+ if (offset >= 0x20) {
+ if (!(s->feat & TMU012_FEAT_3CHAN))
+ hw_error("tmu012_write: Bad channel offset %x\n", (int)offset);
+ return sh_timer_read(s->timer[2], offset - 0x20);
+ }
+
+ if (offset >= 0x14)
+ return sh_timer_read(s->timer[1], offset - 0x14);
+
+ if (offset >= 0x08)
+ return sh_timer_read(s->timer[0], offset - 0x08);
+
+ if (offset == 4)
+ return s->tstr;
+
+ if ((s->feat & TMU012_FEAT_TOCR) && offset == 0)
+ return s->tocr;
+
+ hw_error("tmu012_write: Bad offset %x\n", (int)offset);
+ return 0;
+}
+
+static void tmu012_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ tmu012_state *s = (tmu012_state *)opaque;
+
+#ifdef DEBUG_TIMER
+ printf("tmu012_write 0x%lx 0x%08x\n", (unsigned long) offset, value);
+#endif
+
+ if (offset >= 0x20) {
+ if (!(s->feat & TMU012_FEAT_3CHAN))
+ hw_error("tmu012_write: Bad channel offset %x\n", (int)offset);
+ sh_timer_write(s->timer[2], offset - 0x20, value);
+ return;
+ }
+
+ if (offset >= 0x14) {
+ sh_timer_write(s->timer[1], offset - 0x14, value);
+ return;
+ }
+
+ if (offset >= 0x08) {
+ sh_timer_write(s->timer[0], offset - 0x08, value);
+ return;
+ }
+
+ if (offset == 4) {
+ sh_timer_start_stop(s->timer[0], value & (1 << 0));
+ sh_timer_start_stop(s->timer[1], value & (1 << 1));
+ if (s->feat & TMU012_FEAT_3CHAN)
+ sh_timer_start_stop(s->timer[2], value & (1 << 2));
+ else
+ if (value & (1 << 2))
+ hw_error("tmu012_write: Bad channel\n");
+
+ s->tstr = value;
+ return;
+ }
+
+ if ((s->feat & TMU012_FEAT_TOCR) && offset == 0) {
+ s->tocr = value & (1 << 0);
+ }
+}
+
+static const MemoryRegionOps tmu012_ops = {
+ .read = tmu012_read,
+ .write = tmu012_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+void tmu012_init(MemoryRegion *sysmem, hwaddr base,
+ int feat, uint32_t freq,
+ qemu_irq ch0_irq, qemu_irq ch1_irq,
+ qemu_irq ch2_irq0, qemu_irq ch2_irq1)
+{
+ tmu012_state *s;
+ int timer_feat = (feat & TMU012_FEAT_EXTCLK) ? TIMER_FEAT_EXTCLK : 0;
+
+ s = (tmu012_state *)g_malloc0(sizeof(tmu012_state));
+ s->feat = feat;
+ s->timer[0] = sh_timer_init(freq, timer_feat, ch0_irq);
+ s->timer[1] = sh_timer_init(freq, timer_feat, ch1_irq);
+ if (feat & TMU012_FEAT_3CHAN)
+ s->timer[2] = sh_timer_init(freq, timer_feat | TIMER_FEAT_CAPT,
+ ch2_irq0); /* ch2_irq1 not supported */
+
+ memory_region_init_io(&s->iomem, &tmu012_ops, s,
+ "timer", 0x100000000ULL);
+
+ memory_region_init_alias(&s->iomem_p4, "timer-p4",
+ &s->iomem, 0, 0x1000);
+ memory_region_add_subregion(sysmem, P4ADDR(base), &s->iomem_p4);
+
+ memory_region_init_alias(&s->iomem_a7, "timer-a7",
+ &s->iomem, 0, 0x1000);
+ memory_region_add_subregion(sysmem, A7ADDR(base), &s->iomem_a7);
+ /* ??? Save/restore. */
+}
--- /dev/null
+/*
+ * QEMU Sparc SLAVIO timer controller emulation
+ *
+ * Copyright (c) 2003-2005 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw/sparc/sun4m.h"
+#include "qemu/timer.h"
+#include "hw/ptimer.h"
+#include "hw/sysbus.h"
+#include "trace.h"
+
+/*
+ * Registers of hardware timer in sun4m.
+ *
+ * This is the timer/counter part of chip STP2001 (Slave I/O), also
+ * produced as NCR89C105. See
+ * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C105.txt
+ *
+ * The 31-bit counter is incremented every 500ns by bit 9. Bits 8..0
+ * are zero. Bit 31 is 1 when count has been reached.
+ *
+ * Per-CPU timers interrupt local CPU, system timer uses normal
+ * interrupt routing.
+ *
+ */
+
+#define MAX_CPUS 16
+
+typedef struct CPUTimerState {
+ qemu_irq irq;
+ ptimer_state *timer;
+ uint32_t count, counthigh, reached;
+ /* processor only */
+ uint32_t running;
+ uint64_t limit;
+} CPUTimerState;
+
+typedef struct SLAVIO_TIMERState {
+ SysBusDevice busdev;
+ uint32_t num_cpus;
+ uint32_t cputimer_mode;
+ CPUTimerState cputimer[MAX_CPUS + 1];
+} SLAVIO_TIMERState;
+
+typedef struct TimerContext {
+ MemoryRegion iomem;
+ SLAVIO_TIMERState *s;
+ unsigned int timer_index; /* 0 for system, 1 ... MAX_CPUS for CPU timers */
+} TimerContext;
+
+#define SYS_TIMER_SIZE 0x14
+#define CPU_TIMER_SIZE 0x10
+
+#define TIMER_LIMIT 0
+#define TIMER_COUNTER 1
+#define TIMER_COUNTER_NORST 2
+#define TIMER_STATUS 3
+#define TIMER_MODE 4
+
+#define TIMER_COUNT_MASK32 0xfffffe00
+#define TIMER_LIMIT_MASK32 0x7fffffff
+#define TIMER_MAX_COUNT64 0x7ffffffffffffe00ULL
+#define TIMER_MAX_COUNT32 0x7ffffe00ULL
+#define TIMER_REACHED 0x80000000
+#define TIMER_PERIOD 500ULL // 500ns
+#define LIMIT_TO_PERIODS(l) (((l) >> 9) - 1)
+#define PERIODS_TO_LIMIT(l) (((l) + 1) << 9)
+
+static int slavio_timer_is_user(TimerContext *tc)
+{
+ SLAVIO_TIMERState *s = tc->s;
+ unsigned int timer_index = tc->timer_index;
+
+ return timer_index != 0 && (s->cputimer_mode & (1 << (timer_index - 1)));
+}
+
+// Update count, set irq, update expire_time
+// Convert from ptimer countdown units
+static void slavio_timer_get_out(CPUTimerState *t)
+{
+ uint64_t count, limit;
+
+ if (t->limit == 0) { /* free-run system or processor counter */
+ limit = TIMER_MAX_COUNT32;
+ } else {
+ limit = t->limit;
+ }
+ count = limit - PERIODS_TO_LIMIT(ptimer_get_count(t->timer));
+
+ trace_slavio_timer_get_out(t->limit, t->counthigh, t->count);
+ t->count = count & TIMER_COUNT_MASK32;
+ t->counthigh = count >> 32;
+}
+
+// timer callback
+static void slavio_timer_irq(void *opaque)
+{
+ TimerContext *tc = opaque;
+ SLAVIO_TIMERState *s = tc->s;
+ CPUTimerState *t = &s->cputimer[tc->timer_index];
+
+ slavio_timer_get_out(t);
+ trace_slavio_timer_irq(t->counthigh, t->count);
+ /* if limit is 0 (free-run), there will be no match */
+ if (t->limit != 0) {
+ t->reached = TIMER_REACHED;
+ }
+ /* there is no interrupt if user timer or free-run */
+ if (!slavio_timer_is_user(tc) && t->limit != 0) {
+ qemu_irq_raise(t->irq);
+ }
+}
+
+static uint64_t slavio_timer_mem_readl(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ TimerContext *tc = opaque;
+ SLAVIO_TIMERState *s = tc->s;
+ uint32_t saddr, ret;
+ unsigned int timer_index = tc->timer_index;
+ CPUTimerState *t = &s->cputimer[timer_index];
+
+ saddr = addr >> 2;
+ switch (saddr) {
+ case TIMER_LIMIT:
+ // read limit (system counter mode) or read most signifying
+ // part of counter (user mode)
+ if (slavio_timer_is_user(tc)) {
+ // read user timer MSW
+ slavio_timer_get_out(t);
+ ret = t->counthigh | t->reached;
+ } else {
+ // read limit
+ // clear irq
+ qemu_irq_lower(t->irq);
+ t->reached = 0;
+ ret = t->limit & TIMER_LIMIT_MASK32;
+ }
+ break;
+ case TIMER_COUNTER:
+ // read counter and reached bit (system mode) or read lsbits
+ // of counter (user mode)
+ slavio_timer_get_out(t);
+ if (slavio_timer_is_user(tc)) { // read user timer LSW
+ ret = t->count & TIMER_MAX_COUNT64;
+ } else { // read limit
+ ret = (t->count & TIMER_MAX_COUNT32) |
+ t->reached;
+ }
+ break;
+ case TIMER_STATUS:
+ // only available in processor counter/timer
+ // read start/stop status
+ if (timer_index > 0) {
+ ret = t->running;
+ } else {
+ ret = 0;
+ }
+ break;
+ case TIMER_MODE:
+ // only available in system counter
+ // read user/system mode
+ ret = s->cputimer_mode;
+ break;
+ default:
+ trace_slavio_timer_mem_readl_invalid(addr);
+ ret = 0;
+ break;
+ }
+ trace_slavio_timer_mem_readl(addr, ret);
+ return ret;
+}
+
+static void slavio_timer_mem_writel(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+ TimerContext *tc = opaque;
+ SLAVIO_TIMERState *s = tc->s;
+ uint32_t saddr;
+ unsigned int timer_index = tc->timer_index;
+ CPUTimerState *t = &s->cputimer[timer_index];
+
+ trace_slavio_timer_mem_writel(addr, val);
+ saddr = addr >> 2;
+ switch (saddr) {
+ case TIMER_LIMIT:
+ if (slavio_timer_is_user(tc)) {
+ uint64_t count;
+
+ // set user counter MSW, reset counter
+ t->limit = TIMER_MAX_COUNT64;
+ t->counthigh = val & (TIMER_MAX_COUNT64 >> 32);
+ t->reached = 0;
+ count = ((uint64_t)t->counthigh << 32) | t->count;
+ trace_slavio_timer_mem_writel_limit(timer_index, count);
+ ptimer_set_count(t->timer, LIMIT_TO_PERIODS(t->limit - count));
+ } else {
+ // set limit, reset counter
+ qemu_irq_lower(t->irq);
+ t->limit = val & TIMER_MAX_COUNT32;
+ if (t->timer) {
+ if (t->limit == 0) { /* free-run */
+ ptimer_set_limit(t->timer,
+ LIMIT_TO_PERIODS(TIMER_MAX_COUNT32), 1);
+ } else {
+ ptimer_set_limit(t->timer, LIMIT_TO_PERIODS(t->limit), 1);
+ }
+ }
+ }
+ break;
+ case TIMER_COUNTER:
+ if (slavio_timer_is_user(tc)) {
+ uint64_t count;
+
+ // set user counter LSW, reset counter
+ t->limit = TIMER_MAX_COUNT64;
+ t->count = val & TIMER_MAX_COUNT64;
+ t->reached = 0;
+ count = ((uint64_t)t->counthigh) << 32 | t->count;
+ trace_slavio_timer_mem_writel_limit(timer_index, count);
+ ptimer_set_count(t->timer, LIMIT_TO_PERIODS(t->limit - count));
+ } else {
+ trace_slavio_timer_mem_writel_counter_invalid();
+ }
+ break;
+ case TIMER_COUNTER_NORST:
+ // set limit without resetting counter
+ t->limit = val & TIMER_MAX_COUNT32;
+ if (t->limit == 0) { /* free-run */
+ ptimer_set_limit(t->timer, LIMIT_TO_PERIODS(TIMER_MAX_COUNT32), 0);
+ } else {
+ ptimer_set_limit(t->timer, LIMIT_TO_PERIODS(t->limit), 0);
+ }
+ break;
+ case TIMER_STATUS:
+ if (slavio_timer_is_user(tc)) {
+ // start/stop user counter
+ if ((val & 1) && !t->running) {
+ trace_slavio_timer_mem_writel_status_start(timer_index);
+ ptimer_run(t->timer, 0);
+ t->running = 1;
+ } else if (!(val & 1) && t->running) {
+ trace_slavio_timer_mem_writel_status_stop(timer_index);
+ ptimer_stop(t->timer);
+ t->running = 0;
+ }
+ }
+ break;
+ case TIMER_MODE:
+ if (timer_index == 0) {
+ unsigned int i;
+
+ for (i = 0; i < s->num_cpus; i++) {
+ unsigned int processor = 1 << i;
+ CPUTimerState *curr_timer = &s->cputimer[i + 1];
+
+ // check for a change in timer mode for this processor
+ if ((val & processor) != (s->cputimer_mode & processor)) {
+ if (val & processor) { // counter -> user timer
+ qemu_irq_lower(curr_timer->irq);
+ // counters are always running
+ ptimer_stop(curr_timer->timer);
+ curr_timer->running = 0;
+ // user timer limit is always the same
+ curr_timer->limit = TIMER_MAX_COUNT64;
+ ptimer_set_limit(curr_timer->timer,
+ LIMIT_TO_PERIODS(curr_timer->limit),
+ 1);
+ // set this processors user timer bit in config
+ // register
+ s->cputimer_mode |= processor;
+ trace_slavio_timer_mem_writel_mode_user(timer_index);
+ } else { // user timer -> counter
+ // stop the user timer if it is running
+ if (curr_timer->running) {
+ ptimer_stop(curr_timer->timer);
+ }
+ // start the counter
+ ptimer_run(curr_timer->timer, 0);
+ curr_timer->running = 1;
+ // clear this processors user timer bit in config
+ // register
+ s->cputimer_mode &= ~processor;
+ trace_slavio_timer_mem_writel_mode_counter(timer_index);
+ }
+ }
+ }
+ } else {
+ trace_slavio_timer_mem_writel_mode_invalid();
+ }
+ break;
+ default:
+ trace_slavio_timer_mem_writel_invalid(addr);
+ break;
+ }
+}
+
+static const MemoryRegionOps slavio_timer_mem_ops = {
+ .read = slavio_timer_mem_readl,
+ .write = slavio_timer_mem_writel,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+};
+
+static const VMStateDescription vmstate_timer = {
+ .name ="timer",
+ .version_id = 3,
+ .minimum_version_id = 3,
+ .minimum_version_id_old = 3,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT64(limit, CPUTimerState),
+ VMSTATE_UINT32(count, CPUTimerState),
+ VMSTATE_UINT32(counthigh, CPUTimerState),
+ VMSTATE_UINT32(reached, CPUTimerState),
+ VMSTATE_UINT32(running, CPUTimerState),
+ VMSTATE_PTIMER(timer, CPUTimerState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_slavio_timer = {
+ .name ="slavio_timer",
+ .version_id = 3,
+ .minimum_version_id = 3,
+ .minimum_version_id_old = 3,
+ .fields = (VMStateField []) {
+ VMSTATE_STRUCT_ARRAY(cputimer, SLAVIO_TIMERState, MAX_CPUS + 1, 3,
+ vmstate_timer, CPUTimerState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void slavio_timer_reset(DeviceState *d)
+{
+ SLAVIO_TIMERState *s = container_of(d, SLAVIO_TIMERState, busdev.qdev);
+ unsigned int i;
+ CPUTimerState *curr_timer;
+
+ for (i = 0; i <= MAX_CPUS; i++) {
+ curr_timer = &s->cputimer[i];
+ curr_timer->limit = 0;
+ curr_timer->count = 0;
+ curr_timer->reached = 0;
+ if (i <= s->num_cpus) {
+ ptimer_set_limit(curr_timer->timer,
+ LIMIT_TO_PERIODS(TIMER_MAX_COUNT32), 1);
+ ptimer_run(curr_timer->timer, 0);
+ curr_timer->running = 1;
+ }
+ }
+ s->cputimer_mode = 0;
+}
+
+static int slavio_timer_init1(SysBusDevice *dev)
+{
+ SLAVIO_TIMERState *s = FROM_SYSBUS(SLAVIO_TIMERState, dev);
+ QEMUBH *bh;
+ unsigned int i;
+ TimerContext *tc;
+
+ for (i = 0; i <= MAX_CPUS; i++) {
+ uint64_t size;
+ char timer_name[20];
+
+ tc = g_malloc0(sizeof(TimerContext));
+ tc->s = s;
+ tc->timer_index = i;
+
+ bh = qemu_bh_new(slavio_timer_irq, tc);
+ s->cputimer[i].timer = ptimer_init(bh);
+ ptimer_set_period(s->cputimer[i].timer, TIMER_PERIOD);
+
+ size = i == 0 ? SYS_TIMER_SIZE : CPU_TIMER_SIZE;
+ snprintf(timer_name, sizeof(timer_name), "timer-%i", i);
+ memory_region_init_io(&tc->iomem, &slavio_timer_mem_ops, tc,
+ timer_name, size);
+ sysbus_init_mmio(dev, &tc->iomem);
+
+ sysbus_init_irq(dev, &s->cputimer[i].irq);
+ }
+
+ return 0;
+}
+
+static Property slavio_timer_properties[] = {
+ DEFINE_PROP_UINT32("num_cpus", SLAVIO_TIMERState, num_cpus, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void slavio_timer_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = slavio_timer_init1;
+ dc->reset = slavio_timer_reset;
+ dc->vmsd = &vmstate_slavio_timer;
+ dc->props = slavio_timer_properties;
+}
+
+static const TypeInfo slavio_timer_info = {
+ .name = "slavio_timer",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(SLAVIO_TIMERState),
+ .class_init = slavio_timer_class_init,
+};
+
+static void slavio_timer_register_types(void)
+{
+ type_register_static(&slavio_timer_info);
+}
+
+type_init(slavio_timer_register_types)
--- /dev/null
+/*
+ * Texas Instruments TUSB6010 emulation.
+ * Based on reverse-engineering of a linux driver.
+ *
+ * Copyright (C) 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) version 3 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 "qemu-common.h"
+#include "qemu/timer.h"
+#include "hw/usb.h"
+#include "hw/arm/omap.h"
+#include "hw/irq.h"
+#include "hw/arm/devices.h"
+#include "hw/sysbus.h"
+
+typedef struct TUSBState {
+ SysBusDevice busdev;
+ MemoryRegion iomem[2];
+ qemu_irq irq;
+ MUSBState *musb;
+ QEMUTimer *otg_timer;
+ QEMUTimer *pwr_timer;
+
+ int power;
+ uint32_t scratch;
+ uint16_t test_reset;
+ uint32_t prcm_config;
+ uint32_t prcm_mngmt;
+ uint16_t otg_status;
+ uint32_t dev_config;
+ int host_mode;
+ uint32_t intr;
+ uint32_t intr_ok;
+ uint32_t mask;
+ uint32_t usbip_intr;
+ uint32_t usbip_mask;
+ uint32_t gpio_intr;
+ uint32_t gpio_mask;
+ uint32_t gpio_config;
+ uint32_t dma_intr;
+ uint32_t dma_mask;
+ uint32_t dma_map;
+ uint32_t dma_config;
+ uint32_t ep0_config;
+ uint32_t rx_config[15];
+ uint32_t tx_config[15];
+ uint32_t wkup_mask;
+ uint32_t pullup[2];
+ uint32_t control_config;
+ uint32_t otg_timer_val;
+} TUSBState;
+
+#define TUSB_DEVCLOCK 60000000 /* 60 MHz */
+
+#define TUSB_VLYNQ_CTRL 0x004
+
+/* Mentor Graphics OTG core registers. */
+#define TUSB_BASE_OFFSET 0x400
+
+/* FIFO registers, 32-bit. */
+#define TUSB_FIFO_BASE 0x600
+
+/* Device System & Control registers, 32-bit. */
+#define TUSB_SYS_REG_BASE 0x800
+
+#define TUSB_DEV_CONF (TUSB_SYS_REG_BASE + 0x000)
+#define TUSB_DEV_CONF_USB_HOST_MODE (1 << 16)
+#define TUSB_DEV_CONF_PROD_TEST_MODE (1 << 15)
+#define TUSB_DEV_CONF_SOFT_ID (1 << 1)
+#define TUSB_DEV_CONF_ID_SEL (1 << 0)
+
+#define TUSB_PHY_OTG_CTRL_ENABLE (TUSB_SYS_REG_BASE + 0x004)
+#define TUSB_PHY_OTG_CTRL (TUSB_SYS_REG_BASE + 0x008)
+#define TUSB_PHY_OTG_CTRL_WRPROTECT (0xa5 << 24)
+#define TUSB_PHY_OTG_CTRL_O_ID_PULLUP (1 << 23)
+#define TUSB_PHY_OTG_CTRL_O_VBUS_DET_EN (1 << 19)
+#define TUSB_PHY_OTG_CTRL_O_SESS_END_EN (1 << 18)
+#define TUSB_PHY_OTG_CTRL_TESTM2 (1 << 17)
+#define TUSB_PHY_OTG_CTRL_TESTM1 (1 << 16)
+#define TUSB_PHY_OTG_CTRL_TESTM0 (1 << 15)
+#define TUSB_PHY_OTG_CTRL_TX_DATA2 (1 << 14)
+#define TUSB_PHY_OTG_CTRL_TX_GZ2 (1 << 13)
+#define TUSB_PHY_OTG_CTRL_TX_ENABLE2 (1 << 12)
+#define TUSB_PHY_OTG_CTRL_DM_PULLDOWN (1 << 11)
+#define TUSB_PHY_OTG_CTRL_DP_PULLDOWN (1 << 10)
+#define TUSB_PHY_OTG_CTRL_OSC_EN (1 << 9)
+#define TUSB_PHY_OTG_CTRL_PHYREF_CLK(v) (((v) & 3) << 7)
+#define TUSB_PHY_OTG_CTRL_PD (1 << 6)
+#define TUSB_PHY_OTG_CTRL_PLL_ON (1 << 5)
+#define TUSB_PHY_OTG_CTRL_EXT_RPU (1 << 4)
+#define TUSB_PHY_OTG_CTRL_PWR_GOOD (1 << 3)
+#define TUSB_PHY_OTG_CTRL_RESET (1 << 2)
+#define TUSB_PHY_OTG_CTRL_SUSPENDM (1 << 1)
+#define TUSB_PHY_OTG_CTRL_CLK_MODE (1 << 0)
+
+/* OTG status register */
+#define TUSB_DEV_OTG_STAT (TUSB_SYS_REG_BASE + 0x00c)
+#define TUSB_DEV_OTG_STAT_PWR_CLK_GOOD (1 << 8)
+#define TUSB_DEV_OTG_STAT_SESS_END (1 << 7)
+#define TUSB_DEV_OTG_STAT_SESS_VALID (1 << 6)
+#define TUSB_DEV_OTG_STAT_VBUS_VALID (1 << 5)
+#define TUSB_DEV_OTG_STAT_VBUS_SENSE (1 << 4)
+#define TUSB_DEV_OTG_STAT_ID_STATUS (1 << 3)
+#define TUSB_DEV_OTG_STAT_HOST_DISCON (1 << 2)
+#define TUSB_DEV_OTG_STAT_LINE_STATE (3 << 0)
+#define TUSB_DEV_OTG_STAT_DP_ENABLE (1 << 1)
+#define TUSB_DEV_OTG_STAT_DM_ENABLE (1 << 0)
+
+#define TUSB_DEV_OTG_TIMER (TUSB_SYS_REG_BASE + 0x010)
+#define TUSB_DEV_OTG_TIMER_ENABLE (1 << 31)
+#define TUSB_DEV_OTG_TIMER_VAL(v) ((v) & 0x07ffffff)
+#define TUSB_PRCM_REV (TUSB_SYS_REG_BASE + 0x014)
+
+/* PRCM configuration register */
+#define TUSB_PRCM_CONF (TUSB_SYS_REG_BASE + 0x018)
+#define TUSB_PRCM_CONF_SFW_CPEN (1 << 24)
+#define TUSB_PRCM_CONF_SYS_CLKSEL(v) (((v) & 3) << 16)
+
+/* PRCM management register */
+#define TUSB_PRCM_MNGMT (TUSB_SYS_REG_BASE + 0x01c)
+#define TUSB_PRCM_MNGMT_SRP_FIX_TMR(v) (((v) & 0xf) << 25)
+#define TUSB_PRCM_MNGMT_SRP_FIX_EN (1 << 24)
+#define TUSB_PRCM_MNGMT_VBUS_VAL_TMR(v) (((v) & 0xf) << 20)
+#define TUSB_PRCM_MNGMT_VBUS_VAL_FLT_EN (1 << 19)
+#define TUSB_PRCM_MNGMT_DFT_CLK_DIS (1 << 18)
+#define TUSB_PRCM_MNGMT_VLYNQ_CLK_DIS (1 << 17)
+#define TUSB_PRCM_MNGMT_OTG_SESS_END_EN (1 << 10)
+#define TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN (1 << 9)
+#define TUSB_PRCM_MNGMT_OTG_ID_PULLUP (1 << 8)
+#define TUSB_PRCM_MNGMT_15_SW_EN (1 << 4)
+#define TUSB_PRCM_MNGMT_33_SW_EN (1 << 3)
+#define TUSB_PRCM_MNGMT_5V_CPEN (1 << 2)
+#define TUSB_PRCM_MNGMT_PM_IDLE (1 << 1)
+#define TUSB_PRCM_MNGMT_DEV_IDLE (1 << 0)
+
+/* Wake-up source clear and mask registers */
+#define TUSB_PRCM_WAKEUP_SOURCE (TUSB_SYS_REG_BASE + 0x020)
+#define TUSB_PRCM_WAKEUP_CLEAR (TUSB_SYS_REG_BASE + 0x028)
+#define TUSB_PRCM_WAKEUP_MASK (TUSB_SYS_REG_BASE + 0x02c)
+#define TUSB_PRCM_WAKEUP_RESERVED_BITS (0xffffe << 13)
+#define TUSB_PRCM_WGPIO_7 (1 << 12)
+#define TUSB_PRCM_WGPIO_6 (1 << 11)
+#define TUSB_PRCM_WGPIO_5 (1 << 10)
+#define TUSB_PRCM_WGPIO_4 (1 << 9)
+#define TUSB_PRCM_WGPIO_3 (1 << 8)
+#define TUSB_PRCM_WGPIO_2 (1 << 7)
+#define TUSB_PRCM_WGPIO_1 (1 << 6)
+#define TUSB_PRCM_WGPIO_0 (1 << 5)
+#define TUSB_PRCM_WHOSTDISCON (1 << 4) /* Host disconnect */
+#define TUSB_PRCM_WBUS (1 << 3) /* USB bus resume */
+#define TUSB_PRCM_WNORCS (1 << 2) /* NOR chip select */
+#define TUSB_PRCM_WVBUS (1 << 1) /* OTG PHY VBUS */
+#define TUSB_PRCM_WID (1 << 0) /* OTG PHY ID detect */
+
+#define TUSB_PULLUP_1_CTRL (TUSB_SYS_REG_BASE + 0x030)
+#define TUSB_PULLUP_2_CTRL (TUSB_SYS_REG_BASE + 0x034)
+#define TUSB_INT_CTRL_REV (TUSB_SYS_REG_BASE + 0x038)
+#define TUSB_INT_CTRL_CONF (TUSB_SYS_REG_BASE + 0x03c)
+#define TUSB_USBIP_INT_SRC (TUSB_SYS_REG_BASE + 0x040)
+#define TUSB_USBIP_INT_SET (TUSB_SYS_REG_BASE + 0x044)
+#define TUSB_USBIP_INT_CLEAR (TUSB_SYS_REG_BASE + 0x048)
+#define TUSB_USBIP_INT_MASK (TUSB_SYS_REG_BASE + 0x04c)
+#define TUSB_DMA_INT_SRC (TUSB_SYS_REG_BASE + 0x050)
+#define TUSB_DMA_INT_SET (TUSB_SYS_REG_BASE + 0x054)
+#define TUSB_DMA_INT_CLEAR (TUSB_SYS_REG_BASE + 0x058)
+#define TUSB_DMA_INT_MASK (TUSB_SYS_REG_BASE + 0x05c)
+#define TUSB_GPIO_INT_SRC (TUSB_SYS_REG_BASE + 0x060)
+#define TUSB_GPIO_INT_SET (TUSB_SYS_REG_BASE + 0x064)
+#define TUSB_GPIO_INT_CLEAR (TUSB_SYS_REG_BASE + 0x068)
+#define TUSB_GPIO_INT_MASK (TUSB_SYS_REG_BASE + 0x06c)
+
+/* NOR flash interrupt source registers */
+#define TUSB_INT_SRC (TUSB_SYS_REG_BASE + 0x070)
+#define TUSB_INT_SRC_SET (TUSB_SYS_REG_BASE + 0x074)
+#define TUSB_INT_SRC_CLEAR (TUSB_SYS_REG_BASE + 0x078)
+#define TUSB_INT_MASK (TUSB_SYS_REG_BASE + 0x07c)
+#define TUSB_INT_SRC_TXRX_DMA_DONE (1 << 24)
+#define TUSB_INT_SRC_USB_IP_CORE (1 << 17)
+#define TUSB_INT_SRC_OTG_TIMEOUT (1 << 16)
+#define TUSB_INT_SRC_VBUS_SENSE_CHNG (1 << 15)
+#define TUSB_INT_SRC_ID_STATUS_CHNG (1 << 14)
+#define TUSB_INT_SRC_DEV_WAKEUP (1 << 13)
+#define TUSB_INT_SRC_DEV_READY (1 << 12)
+#define TUSB_INT_SRC_USB_IP_TX (1 << 9)
+#define TUSB_INT_SRC_USB_IP_RX (1 << 8)
+#define TUSB_INT_SRC_USB_IP_VBUS_ERR (1 << 7)
+#define TUSB_INT_SRC_USB_IP_VBUS_REQ (1 << 6)
+#define TUSB_INT_SRC_USB_IP_DISCON (1 << 5)
+#define TUSB_INT_SRC_USB_IP_CONN (1 << 4)
+#define TUSB_INT_SRC_USB_IP_SOF (1 << 3)
+#define TUSB_INT_SRC_USB_IP_RST_BABBLE (1 << 2)
+#define TUSB_INT_SRC_USB_IP_RESUME (1 << 1)
+#define TUSB_INT_SRC_USB_IP_SUSPEND (1 << 0)
+
+#define TUSB_GPIO_REV (TUSB_SYS_REG_BASE + 0x080)
+#define TUSB_GPIO_CONF (TUSB_SYS_REG_BASE + 0x084)
+#define TUSB_DMA_CTRL_REV (TUSB_SYS_REG_BASE + 0x100)
+#define TUSB_DMA_REQ_CONF (TUSB_SYS_REG_BASE + 0x104)
+#define TUSB_EP0_CONF (TUSB_SYS_REG_BASE + 0x108)
+#define TUSB_EP_IN_SIZE (TUSB_SYS_REG_BASE + 0x10c)
+#define TUSB_DMA_EP_MAP (TUSB_SYS_REG_BASE + 0x148)
+#define TUSB_EP_OUT_SIZE (TUSB_SYS_REG_BASE + 0x14c)
+#define TUSB_EP_MAX_PACKET_SIZE_OFFSET (TUSB_SYS_REG_BASE + 0x188)
+#define TUSB_SCRATCH_PAD (TUSB_SYS_REG_BASE + 0x1c4)
+#define TUSB_WAIT_COUNT (TUSB_SYS_REG_BASE + 0x1c8)
+#define TUSB_PROD_TEST_RESET (TUSB_SYS_REG_BASE + 0x1d8)
+
+#define TUSB_DIDR1_LO (TUSB_SYS_REG_BASE + 0x1f8)
+#define TUSB_DIDR1_HI (TUSB_SYS_REG_BASE + 0x1fc)
+
+/* Device System & Control register bitfields */
+#define TUSB_INT_CTRL_CONF_INT_RLCYC(v) (((v) & 0x7) << 18)
+#define TUSB_INT_CTRL_CONF_INT_POLARITY (1 << 17)
+#define TUSB_INT_CTRL_CONF_INT_MODE (1 << 16)
+#define TUSB_GPIO_CONF_DMAREQ(v) (((v) & 0x3f) << 24)
+#define TUSB_DMA_REQ_CONF_BURST_SIZE(v) (((v) & 3) << 26)
+#define TUSB_DMA_REQ_CONF_DMA_RQ_EN(v) (((v) & 0x3f) << 20)
+#define TUSB_DMA_REQ_CONF_DMA_RQ_ASR(v) (((v) & 0xf) << 16)
+#define TUSB_EP0_CONFIG_SW_EN (1 << 8)
+#define TUSB_EP0_CONFIG_DIR_TX (1 << 7)
+#define TUSB_EP0_CONFIG_XFR_SIZE(v) ((v) & 0x7f)
+#define TUSB_EP_CONFIG_SW_EN (1 << 31)
+#define TUSB_EP_CONFIG_XFR_SIZE(v) ((v) & 0x7fffffff)
+#define TUSB_PROD_TEST_RESET_VAL 0xa596
+
+static void tusb_intr_update(TUSBState *s)
+{
+ if (s->control_config & TUSB_INT_CTRL_CONF_INT_POLARITY)
+ qemu_set_irq(s->irq, s->intr & ~s->mask & s->intr_ok);
+ else
+ qemu_set_irq(s->irq, (!(s->intr & ~s->mask)) & s->intr_ok);
+}
+
+static void tusb_usbip_intr_update(TUSBState *s)
+{
+ /* TX interrupt in the MUSB */
+ if (s->usbip_intr & 0x0000ffff & ~s->usbip_mask)
+ s->intr |= TUSB_INT_SRC_USB_IP_TX;
+ else
+ s->intr &= ~TUSB_INT_SRC_USB_IP_TX;
+
+ /* RX interrupt in the MUSB */
+ if (s->usbip_intr & 0xffff0000 & ~s->usbip_mask)
+ s->intr |= TUSB_INT_SRC_USB_IP_RX;
+ else
+ s->intr &= ~TUSB_INT_SRC_USB_IP_RX;
+
+ /* XXX: What about TUSB_INT_SRC_USB_IP_CORE? */
+
+ tusb_intr_update(s);
+}
+
+static void tusb_dma_intr_update(TUSBState *s)
+{
+ if (s->dma_intr & ~s->dma_mask)
+ s->intr |= TUSB_INT_SRC_TXRX_DMA_DONE;
+ else
+ s->intr &= ~TUSB_INT_SRC_TXRX_DMA_DONE;
+
+ tusb_intr_update(s);
+}
+
+static void tusb_gpio_intr_update(TUSBState *s)
+{
+ /* TODO: How is this signalled? */
+}
+
+extern CPUReadMemoryFunc * const musb_read[];
+extern CPUWriteMemoryFunc * const musb_write[];
+
+static uint32_t tusb_async_readb(void *opaque, hwaddr addr)
+{
+ TUSBState *s = (TUSBState *) opaque;
+
+ switch (addr & 0xfff) {
+ case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff):
+ return musb_read[0](s->musb, addr & 0x1ff);
+
+ case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff):
+ return musb_read[0](s->musb, 0x20 + ((addr >> 3) & 0x3c));
+ }
+
+ printf("%s: unknown register at %03x\n",
+ __FUNCTION__, (int) (addr & 0xfff));
+ return 0;
+}
+
+static uint32_t tusb_async_readh(void *opaque, hwaddr addr)
+{
+ TUSBState *s = (TUSBState *) opaque;
+
+ switch (addr & 0xfff) {
+ case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff):
+ return musb_read[1](s->musb, addr & 0x1ff);
+
+ case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff):
+ return musb_read[1](s->musb, 0x20 + ((addr >> 3) & 0x3c));
+ }
+
+ printf("%s: unknown register at %03x\n",
+ __FUNCTION__, (int) (addr & 0xfff));
+ return 0;
+}
+
+static uint32_t tusb_async_readw(void *opaque, hwaddr addr)
+{
+ TUSBState *s = (TUSBState *) opaque;
+ int offset = addr & 0xfff;
+ int epnum;
+ uint32_t ret;
+
+ switch (offset) {
+ case TUSB_DEV_CONF:
+ return s->dev_config;
+
+ case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff):
+ return musb_read[2](s->musb, offset & 0x1ff);
+
+ case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff):
+ return musb_read[2](s->musb, 0x20 + ((addr >> 3) & 0x3c));
+
+ case TUSB_PHY_OTG_CTRL_ENABLE:
+ case TUSB_PHY_OTG_CTRL:
+ return 0x00; /* TODO */
+
+ case TUSB_DEV_OTG_STAT:
+ ret = s->otg_status;
+#if 0
+ if (!(s->prcm_mngmt & TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN))
+ ret &= ~TUSB_DEV_OTG_STAT_VBUS_VALID;
+#endif
+ return ret;
+ case TUSB_DEV_OTG_TIMER:
+ return s->otg_timer_val;
+
+ case TUSB_PRCM_REV:
+ return 0x20;
+ case TUSB_PRCM_CONF:
+ return s->prcm_config;
+ case TUSB_PRCM_MNGMT:
+ return s->prcm_mngmt;
+ case TUSB_PRCM_WAKEUP_SOURCE:
+ case TUSB_PRCM_WAKEUP_CLEAR: /* TODO: What does this one return? */
+ return 0x00000000;
+ case TUSB_PRCM_WAKEUP_MASK:
+ return s->wkup_mask;
+
+ case TUSB_PULLUP_1_CTRL:
+ return s->pullup[0];
+ case TUSB_PULLUP_2_CTRL:
+ return s->pullup[1];
+
+ case TUSB_INT_CTRL_REV:
+ return 0x20;
+ case TUSB_INT_CTRL_CONF:
+ return s->control_config;
+
+ case TUSB_USBIP_INT_SRC:
+ case TUSB_USBIP_INT_SET: /* TODO: What do these two return? */
+ case TUSB_USBIP_INT_CLEAR:
+ return s->usbip_intr;
+ case TUSB_USBIP_INT_MASK:
+ return s->usbip_mask;
+
+ case TUSB_DMA_INT_SRC:
+ case TUSB_DMA_INT_SET: /* TODO: What do these two return? */
+ case TUSB_DMA_INT_CLEAR:
+ return s->dma_intr;
+ case TUSB_DMA_INT_MASK:
+ return s->dma_mask;
+
+ case TUSB_GPIO_INT_SRC: /* TODO: What do these two return? */
+ case TUSB_GPIO_INT_SET:
+ case TUSB_GPIO_INT_CLEAR:
+ return s->gpio_intr;
+ case TUSB_GPIO_INT_MASK:
+ return s->gpio_mask;
+
+ case TUSB_INT_SRC:
+ case TUSB_INT_SRC_SET: /* TODO: What do these two return? */
+ case TUSB_INT_SRC_CLEAR:
+ return s->intr;
+ case TUSB_INT_MASK:
+ return s->mask;
+
+ case TUSB_GPIO_REV:
+ return 0x30;
+ case TUSB_GPIO_CONF:
+ return s->gpio_config;
+
+ case TUSB_DMA_CTRL_REV:
+ return 0x30;
+ case TUSB_DMA_REQ_CONF:
+ return s->dma_config;
+ case TUSB_EP0_CONF:
+ return s->ep0_config;
+ case TUSB_EP_IN_SIZE ... (TUSB_EP_IN_SIZE + 0x3b):
+ epnum = (offset - TUSB_EP_IN_SIZE) >> 2;
+ return s->tx_config[epnum];
+ case TUSB_DMA_EP_MAP:
+ return s->dma_map;
+ case TUSB_EP_OUT_SIZE ... (TUSB_EP_OUT_SIZE + 0x3b):
+ epnum = (offset - TUSB_EP_OUT_SIZE) >> 2;
+ return s->rx_config[epnum];
+ case TUSB_EP_MAX_PACKET_SIZE_OFFSET ...
+ (TUSB_EP_MAX_PACKET_SIZE_OFFSET + 0x3b):
+ return 0x00000000; /* TODO */
+ case TUSB_WAIT_COUNT:
+ return 0x00; /* TODO */
+
+ case TUSB_SCRATCH_PAD:
+ return s->scratch;
+
+ case TUSB_PROD_TEST_RESET:
+ return s->test_reset;
+
+ /* DIE IDs */
+ case TUSB_DIDR1_LO:
+ return 0xa9453c59;
+ case TUSB_DIDR1_HI:
+ return 0x54059adf;
+ }
+
+ printf("%s: unknown register at %03x\n", __FUNCTION__, offset);
+ return 0;
+}
+
+static void tusb_async_writeb(void *opaque, hwaddr addr,
+ uint32_t value)
+{
+ TUSBState *s = (TUSBState *) opaque;
+
+ switch (addr & 0xfff) {
+ case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff):
+ musb_write[0](s->musb, addr & 0x1ff, value);
+ break;
+
+ case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff):
+ musb_write[0](s->musb, 0x20 + ((addr >> 3) & 0x3c), value);
+ break;
+
+ default:
+ printf("%s: unknown register at %03x\n",
+ __FUNCTION__, (int) (addr & 0xfff));
+ return;
+ }
+}
+
+static void tusb_async_writeh(void *opaque, hwaddr addr,
+ uint32_t value)
+{
+ TUSBState *s = (TUSBState *) opaque;
+
+ switch (addr & 0xfff) {
+ case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff):
+ musb_write[1](s->musb, addr & 0x1ff, value);
+ break;
+
+ case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff):
+ musb_write[1](s->musb, 0x20 + ((addr >> 3) & 0x3c), value);
+ break;
+
+ default:
+ printf("%s: unknown register at %03x\n",
+ __FUNCTION__, (int) (addr & 0xfff));
+ return;
+ }
+}
+
+static void tusb_async_writew(void *opaque, hwaddr addr,
+ uint32_t value)
+{
+ TUSBState *s = (TUSBState *) opaque;
+ int offset = addr & 0xfff;
+ int epnum;
+
+ switch (offset) {
+ case TUSB_VLYNQ_CTRL:
+ break;
+
+ case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff):
+ musb_write[2](s->musb, offset & 0x1ff, value);
+ break;
+
+ case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff):
+ musb_write[2](s->musb, 0x20 + ((addr >> 3) & 0x3c), value);
+ break;
+
+ case TUSB_DEV_CONF:
+ s->dev_config = value;
+ s->host_mode = (value & TUSB_DEV_CONF_USB_HOST_MODE);
+ if (value & TUSB_DEV_CONF_PROD_TEST_MODE)
+ hw_error("%s: Product Test mode not allowed\n", __FUNCTION__);
+ break;
+
+ case TUSB_PHY_OTG_CTRL_ENABLE:
+ case TUSB_PHY_OTG_CTRL:
+ return; /* TODO */
+ case TUSB_DEV_OTG_TIMER:
+ s->otg_timer_val = value;
+ if (value & TUSB_DEV_OTG_TIMER_ENABLE)
+ qemu_mod_timer(s->otg_timer, qemu_get_clock_ns(vm_clock) +
+ muldiv64(TUSB_DEV_OTG_TIMER_VAL(value),
+ get_ticks_per_sec(), TUSB_DEVCLOCK));
+ else
+ qemu_del_timer(s->otg_timer);
+ break;
+
+ case TUSB_PRCM_CONF:
+ s->prcm_config = value;
+ break;
+ case TUSB_PRCM_MNGMT:
+ s->prcm_mngmt = value;
+ break;
+ case TUSB_PRCM_WAKEUP_CLEAR:
+ break;
+ case TUSB_PRCM_WAKEUP_MASK:
+ s->wkup_mask = value;
+ break;
+
+ case TUSB_PULLUP_1_CTRL:
+ s->pullup[0] = value;
+ break;
+ case TUSB_PULLUP_2_CTRL:
+ s->pullup[1] = value;
+ break;
+ case TUSB_INT_CTRL_CONF:
+ s->control_config = value;
+ tusb_intr_update(s);
+ break;
+
+ case TUSB_USBIP_INT_SET:
+ s->usbip_intr |= value;
+ tusb_usbip_intr_update(s);
+ break;
+ case TUSB_USBIP_INT_CLEAR:
+ s->usbip_intr &= ~value;
+ tusb_usbip_intr_update(s);
+ musb_core_intr_clear(s->musb, ~value);
+ break;
+ case TUSB_USBIP_INT_MASK:
+ s->usbip_mask = value;
+ tusb_usbip_intr_update(s);
+ break;
+
+ case TUSB_DMA_INT_SET:
+ s->dma_intr |= value;
+ tusb_dma_intr_update(s);
+ break;
+ case TUSB_DMA_INT_CLEAR:
+ s->dma_intr &= ~value;
+ tusb_dma_intr_update(s);
+ break;
+ case TUSB_DMA_INT_MASK:
+ s->dma_mask = value;
+ tusb_dma_intr_update(s);
+ break;
+
+ case TUSB_GPIO_INT_SET:
+ s->gpio_intr |= value;
+ tusb_gpio_intr_update(s);
+ break;
+ case TUSB_GPIO_INT_CLEAR:
+ s->gpio_intr &= ~value;
+ tusb_gpio_intr_update(s);
+ break;
+ case TUSB_GPIO_INT_MASK:
+ s->gpio_mask = value;
+ tusb_gpio_intr_update(s);
+ break;
+
+ case TUSB_INT_SRC_SET:
+ s->intr |= value;
+ tusb_intr_update(s);
+ break;
+ case TUSB_INT_SRC_CLEAR:
+ s->intr &= ~value;
+ tusb_intr_update(s);
+ break;
+ case TUSB_INT_MASK:
+ s->mask = value;
+ tusb_intr_update(s);
+ break;
+
+ case TUSB_GPIO_CONF:
+ s->gpio_config = value;
+ break;
+ case TUSB_DMA_REQ_CONF:
+ s->dma_config = value;
+ break;
+ case TUSB_EP0_CONF:
+ s->ep0_config = value & 0x1ff;
+ musb_set_size(s->musb, 0, TUSB_EP0_CONFIG_XFR_SIZE(value),
+ value & TUSB_EP0_CONFIG_DIR_TX);
+ break;
+ case TUSB_EP_IN_SIZE ... (TUSB_EP_IN_SIZE + 0x3b):
+ epnum = (offset - TUSB_EP_IN_SIZE) >> 2;
+ s->tx_config[epnum] = value;
+ musb_set_size(s->musb, epnum + 1, TUSB_EP_CONFIG_XFR_SIZE(value), 1);
+ break;
+ case TUSB_DMA_EP_MAP:
+ s->dma_map = value;
+ break;
+ case TUSB_EP_OUT_SIZE ... (TUSB_EP_OUT_SIZE + 0x3b):
+ epnum = (offset - TUSB_EP_OUT_SIZE) >> 2;
+ s->rx_config[epnum] = value;
+ musb_set_size(s->musb, epnum + 1, TUSB_EP_CONFIG_XFR_SIZE(value), 0);
+ break;
+ case TUSB_EP_MAX_PACKET_SIZE_OFFSET ...
+ (TUSB_EP_MAX_PACKET_SIZE_OFFSET + 0x3b):
+ return; /* TODO */
+ case TUSB_WAIT_COUNT:
+ return; /* TODO */
+
+ case TUSB_SCRATCH_PAD:
+ s->scratch = value;
+ break;
+
+ case TUSB_PROD_TEST_RESET:
+ s->test_reset = value;
+ break;
+
+ default:
+ printf("%s: unknown register at %03x\n", __FUNCTION__, offset);
+ return;
+ }
+}
+
+static const MemoryRegionOps tusb_async_ops = {
+ .old_mmio = {
+ .read = { tusb_async_readb, tusb_async_readh, tusb_async_readw, },
+ .write = { tusb_async_writeb, tusb_async_writeh, tusb_async_writew, },
+ },
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void tusb_otg_tick(void *opaque)
+{
+ TUSBState *s = (TUSBState *) opaque;
+
+ s->otg_timer_val = 0;
+ s->intr |= TUSB_INT_SRC_OTG_TIMEOUT;
+ tusb_intr_update(s);
+}
+
+static void tusb_power_tick(void *opaque)
+{
+ TUSBState *s = (TUSBState *) opaque;
+
+ if (s->power) {
+ s->intr_ok = ~0;
+ tusb_intr_update(s);
+ }
+}
+
+static void tusb_musb_core_intr(void *opaque, int source, int level)
+{
+ TUSBState *s = (TUSBState *) opaque;
+ uint16_t otg_status = s->otg_status;
+
+ switch (source) {
+ case musb_set_vbus:
+ if (level)
+ otg_status |= TUSB_DEV_OTG_STAT_VBUS_VALID;
+ else
+ otg_status &= ~TUSB_DEV_OTG_STAT_VBUS_VALID;
+
+ /* XXX: only if TUSB_PHY_OTG_CTRL_OTG_VBUS_DET_EN set? */
+ /* XXX: only if TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN set? */
+ if (s->otg_status != otg_status) {
+ s->otg_status = otg_status;
+ s->intr |= TUSB_INT_SRC_VBUS_SENSE_CHNG;
+ tusb_intr_update(s);
+ }
+ break;
+
+ case musb_set_session:
+ /* XXX: only if TUSB_PHY_OTG_CTRL_OTG_SESS_END_EN set? */
+ /* XXX: only if TUSB_PRCM_MNGMT_OTG_SESS_END_EN set? */
+ if (level) {
+ s->otg_status |= TUSB_DEV_OTG_STAT_SESS_VALID;
+ s->otg_status &= ~TUSB_DEV_OTG_STAT_SESS_END;
+ } else {
+ s->otg_status &= ~TUSB_DEV_OTG_STAT_SESS_VALID;
+ s->otg_status |= TUSB_DEV_OTG_STAT_SESS_END;
+ }
+
+ /* XXX: some IRQ or anything? */
+ break;
+
+ case musb_irq_tx:
+ case musb_irq_rx:
+ s->usbip_intr = musb_core_intr_get(s->musb);
+ /* Fall through. */
+ default:
+ if (level)
+ s->intr |= 1 << source;
+ else
+ s->intr &= ~(1 << source);
+ tusb_intr_update(s);
+ break;
+ }
+}
+
+static void tusb6010_power(TUSBState *s, int on)
+{
+ if (!on) {
+ s->power = 0;
+ } else if (!s->power && on) {
+ s->power = 1;
+ /* Pull the interrupt down after TUSB6010 comes up. */
+ s->intr_ok = 0;
+ tusb_intr_update(s);
+ qemu_mod_timer(s->pwr_timer,
+ qemu_get_clock_ns(vm_clock) + get_ticks_per_sec() / 2);
+ }
+}
+
+static void tusb6010_irq(void *opaque, int source, int level)
+{
+ if (source) {
+ tusb_musb_core_intr(opaque, source - 1, level);
+ } else {
+ tusb6010_power(opaque, level);
+ }
+}
+
+static void tusb6010_reset(DeviceState *dev)
+{
+ TUSBState *s = FROM_SYSBUS(TUSBState, SYS_BUS_DEVICE(dev));
+ int i;
+
+ s->test_reset = TUSB_PROD_TEST_RESET_VAL;
+ s->host_mode = 0;
+ s->dev_config = 0;
+ s->otg_status = 0; /* !TUSB_DEV_OTG_STAT_ID_STATUS means host mode */
+ s->power = 0;
+ s->mask = 0xffffffff;
+ s->intr = 0x00000000;
+ s->otg_timer_val = 0;
+ s->scratch = 0;
+ s->prcm_config = 0;
+ s->prcm_mngmt = 0;
+ s->intr_ok = 0;
+ s->usbip_intr = 0;
+ s->usbip_mask = 0;
+ s->gpio_intr = 0;
+ s->gpio_mask = 0;
+ s->gpio_config = 0;
+ s->dma_intr = 0;
+ s->dma_mask = 0;
+ s->dma_map = 0;
+ s->dma_config = 0;
+ s->ep0_config = 0;
+ s->wkup_mask = 0;
+ s->pullup[0] = s->pullup[1] = 0;
+ s->control_config = 0;
+ for (i = 0; i < 15; i++) {
+ s->rx_config[i] = s->tx_config[i] = 0;
+ }
+ musb_reset(s->musb);
+}
+
+static int tusb6010_init(SysBusDevice *dev)
+{
+ TUSBState *s = FROM_SYSBUS(TUSBState, dev);
+ s->otg_timer = qemu_new_timer_ns(vm_clock, tusb_otg_tick, s);
+ s->pwr_timer = qemu_new_timer_ns(vm_clock, tusb_power_tick, s);
+ memory_region_init_io(&s->iomem[1], &tusb_async_ops, s, "tusb-async",
+ UINT32_MAX);
+ sysbus_init_mmio(dev, &s->iomem[0]);
+ sysbus_init_mmio(dev, &s->iomem[1]);
+ sysbus_init_irq(dev, &s->irq);
+ qdev_init_gpio_in(&dev->qdev, tusb6010_irq, musb_irq_max + 1);
+ s->musb = musb_init(&dev->qdev, 1);
+ return 0;
+}
+
+static void tusb6010_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = tusb6010_init;
+ dc->reset = tusb6010_reset;
+}
+
+static const TypeInfo tusb6010_info = {
+ .name = "tusb6010",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(TUSBState),
+ .class_init = tusb6010_class_init,
+};
+
+static void tusb6010_register_types(void)
+{
+ type_register_static(&tusb6010_info);
+}
+
+type_init(tusb6010_register_types)
+++ /dev/null
-/*
- * TI TSC2102 (touchscreen/sensors/audio controller) emulator.
- * TI TSC2301 (touchscreen/sensors/keypad).
- *
- * Copyright (c) 2006 Andrzej Zaborowski <balrog@zabor.org>
- * Copyright (C) 2008 Nokia Corporation
- *
- * 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) version 3 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/hw.h"
-#include "audio/audio.h"
-#include "qemu/timer.h"
-#include "ui/console.h"
-#include "hw/arm/omap.h" /* For I2SCodec and uWireSlave */
-#include "hw/arm/devices.h"
-
-#define TSC_DATA_REGISTERS_PAGE 0x0
-#define TSC_CONTROL_REGISTERS_PAGE 0x1
-#define TSC_AUDIO_REGISTERS_PAGE 0x2
-
-#define TSC_VERBOSE
-
-#define TSC_CUT_RESOLUTION(value, p) ((value) >> (16 - resolution[p]))
-
-typedef struct {
- qemu_irq pint;
- qemu_irq kbint;
- qemu_irq davint;
- QEMUTimer *timer;
- QEMUSoundCard card;
- uWireSlave chip;
- I2SCodec codec;
- uint8_t in_fifo[16384];
- uint8_t out_fifo[16384];
- uint16_t model;
-
- int x, y;
- int pressure;
-
- int state, page, offset, irq;
- uint16_t command, dav;
-
- int busy;
- int enabled;
- int host_mode;
- int function;
- int nextfunction;
- int precision;
- int nextprecision;
- int filter;
- int pin_func;
- int ref;
- int timing;
- int noise;
-
- uint16_t audio_ctrl1;
- uint16_t audio_ctrl2;
- uint16_t audio_ctrl3;
- uint16_t pll[3];
- uint16_t volume;
- int64_t volume_change;
- int softstep;
- uint16_t dac_power;
- int64_t powerdown;
- uint16_t filter_data[0x14];
-
- const char *name;
- SWVoiceIn *adc_voice[1];
- SWVoiceOut *dac_voice[1];
- int i2s_rx_rate;
- int i2s_tx_rate;
-
- int tr[8];
-
- struct {
- uint16_t down;
- uint16_t mask;
- int scan;
- int debounce;
- int mode;
- int intr;
- } kb;
-} TSC210xState;
-
-static const int resolution[4] = { 12, 8, 10, 12 };
-
-#define TSC_MODE_NO_SCAN 0x0
-#define TSC_MODE_XY_SCAN 0x1
-#define TSC_MODE_XYZ_SCAN 0x2
-#define TSC_MODE_X 0x3
-#define TSC_MODE_Y 0x4
-#define TSC_MODE_Z 0x5
-#define TSC_MODE_BAT1 0x6
-#define TSC_MODE_BAT2 0x7
-#define TSC_MODE_AUX 0x8
-#define TSC_MODE_AUX_SCAN 0x9
-#define TSC_MODE_TEMP1 0xa
-#define TSC_MODE_PORT_SCAN 0xb
-#define TSC_MODE_TEMP2 0xc
-#define TSC_MODE_XX_DRV 0xd
-#define TSC_MODE_YY_DRV 0xe
-#define TSC_MODE_YX_DRV 0xf
-
-static const uint16_t mode_regs[16] = {
- 0x0000, /* No scan */
- 0x0600, /* X, Y scan */
- 0x0780, /* X, Y, Z scan */
- 0x0400, /* X */
- 0x0200, /* Y */
- 0x0180, /* Z */
- 0x0040, /* BAT1 */
- 0x0030, /* BAT2 */
- 0x0010, /* AUX */
- 0x0010, /* AUX scan */
- 0x0004, /* TEMP1 */
- 0x0070, /* Port scan */
- 0x0002, /* TEMP2 */
- 0x0000, /* X+, X- drivers */
- 0x0000, /* Y+, Y- drivers */
- 0x0000, /* Y+, X- drivers */
-};
-
-#define X_TRANSFORM(s) \
- ((s->y * s->tr[0] - s->x * s->tr[1]) / s->tr[2] + s->tr[3])
-#define Y_TRANSFORM(s) \
- ((s->y * s->tr[4] - s->x * s->tr[5]) / s->tr[6] + s->tr[7])
-#define Z1_TRANSFORM(s) \
- ((400 - ((s)->x >> 7) + ((s)->pressure << 10)) << 4)
-#define Z2_TRANSFORM(s) \
- ((4000 + ((s)->y >> 7) - ((s)->pressure << 10)) << 4)
-
-#define BAT1_VAL 0x8660
-#define BAT2_VAL 0x0000
-#define AUX1_VAL 0x35c0
-#define AUX2_VAL 0xffff
-#define TEMP1_VAL 0x8c70
-#define TEMP2_VAL 0xa5b0
-
-#define TSC_POWEROFF_DELAY 50
-#define TSC_SOFTSTEP_DELAY 50
-
-static void tsc210x_reset(TSC210xState *s)
-{
- s->state = 0;
- s->pin_func = 2;
- s->enabled = 0;
- s->busy = 0;
- s->nextfunction = 0;
- s->ref = 0;
- s->timing = 0;
- s->irq = 0;
- s->dav = 0;
-
- s->audio_ctrl1 = 0x0000;
- s->audio_ctrl2 = 0x4410;
- s->audio_ctrl3 = 0x0000;
- s->pll[0] = 0x1004;
- s->pll[1] = 0x0000;
- s->pll[2] = 0x1fff;
- s->volume = 0xffff;
- s->dac_power = 0x8540;
- s->softstep = 1;
- s->volume_change = 0;
- s->powerdown = 0;
- s->filter_data[0x00] = 0x6be3;
- s->filter_data[0x01] = 0x9666;
- s->filter_data[0x02] = 0x675d;
- s->filter_data[0x03] = 0x6be3;
- s->filter_data[0x04] = 0x9666;
- s->filter_data[0x05] = 0x675d;
- s->filter_data[0x06] = 0x7d83;
- s->filter_data[0x07] = 0x84ee;
- s->filter_data[0x08] = 0x7d83;
- s->filter_data[0x09] = 0x84ee;
- s->filter_data[0x0a] = 0x6be3;
- s->filter_data[0x0b] = 0x9666;
- s->filter_data[0x0c] = 0x675d;
- s->filter_data[0x0d] = 0x6be3;
- s->filter_data[0x0e] = 0x9666;
- s->filter_data[0x0f] = 0x675d;
- s->filter_data[0x10] = 0x7d83;
- s->filter_data[0x11] = 0x84ee;
- s->filter_data[0x12] = 0x7d83;
- s->filter_data[0x13] = 0x84ee;
-
- s->i2s_tx_rate = 0;
- s->i2s_rx_rate = 0;
-
- s->kb.scan = 1;
- s->kb.debounce = 0;
- s->kb.mask = 0x0000;
- s->kb.mode = 3;
- s->kb.intr = 0;
-
- qemu_set_irq(s->pint, !s->irq);
- qemu_set_irq(s->davint, !s->dav);
- qemu_irq_raise(s->kbint);
-}
-
-typedef struct {
- int rate;
- int dsor;
- int fsref;
-} TSC210xRateInfo;
-
-/* { rate, dsor, fsref } */
-static const TSC210xRateInfo tsc2101_rates[] = {
- /* Fsref / 6.0 */
- { 7350, 7, 1 },
- { 8000, 7, 0 },
- /* Fsref / 5.5 */
- { 8018, 6, 1 },
- { 8727, 6, 0 },
- /* Fsref / 5.0 */
- { 8820, 5, 1 },
- { 9600, 5, 0 },
- /* Fsref / 4.0 */
- { 11025, 4, 1 },
- { 12000, 4, 0 },
- /* Fsref / 3.0 */
- { 14700, 3, 1 },
- { 16000, 3, 0 },
- /* Fsref / 2.0 */
- { 22050, 2, 1 },
- { 24000, 2, 0 },
- /* Fsref / 1.5 */
- { 29400, 1, 1 },
- { 32000, 1, 0 },
- /* Fsref */
- { 44100, 0, 1 },
- { 48000, 0, 0 },
-
- { 0, 0, 0 },
-};
-
-/* { rate, dsor, fsref } */
-static const TSC210xRateInfo tsc2102_rates[] = {
- /* Fsref / 6.0 */
- { 7350, 63, 1 },
- { 8000, 63, 0 },
- /* Fsref / 6.0 */
- { 7350, 54, 1 },
- { 8000, 54, 0 },
- /* Fsref / 5.0 */
- { 8820, 45, 1 },
- { 9600, 45, 0 },
- /* Fsref / 4.0 */
- { 11025, 36, 1 },
- { 12000, 36, 0 },
- /* Fsref / 3.0 */
- { 14700, 27, 1 },
- { 16000, 27, 0 },
- /* Fsref / 2.0 */
- { 22050, 18, 1 },
- { 24000, 18, 0 },
- /* Fsref / 1.5 */
- { 29400, 9, 1 },
- { 32000, 9, 0 },
- /* Fsref */
- { 44100, 0, 1 },
- { 48000, 0, 0 },
-
- { 0, 0, 0 },
-};
-
-static inline void tsc210x_out_flush(TSC210xState *s, int len)
-{
- uint8_t *data = s->codec.out.fifo + s->codec.out.start;
- uint8_t *end = data + len;
-
- while (data < end)
- data += AUD_write(s->dac_voice[0], data, end - data) ?: (end - data);
-
- s->codec.out.len -= len;
- if (s->codec.out.len)
- memmove(s->codec.out.fifo, end, s->codec.out.len);
- s->codec.out.start = 0;
-}
-
-static void tsc210x_audio_out_cb(TSC210xState *s, int free_b)
-{
- if (s->codec.out.len >= free_b) {
- tsc210x_out_flush(s, free_b);
- return;
- }
-
- s->codec.out.size = MIN(free_b, 16384);
- qemu_irq_raise(s->codec.tx_start);
-}
-
-static void tsc2102_audio_rate_update(TSC210xState *s)
-{
- const TSC210xRateInfo *rate;
-
- s->codec.tx_rate = 0;
- s->codec.rx_rate = 0;
- if (s->dac_power & (1 << 15)) /* PWDNC */
- return;
-
- for (rate = tsc2102_rates; rate->rate; rate ++)
- if (rate->dsor == (s->audio_ctrl1 & 0x3f) && /* DACFS */
- rate->fsref == ((s->audio_ctrl3 >> 13) & 1))/* REFFS */
- break;
- if (!rate->rate) {
- printf("%s: unknown sampling rate configured\n", __FUNCTION__);
- return;
- }
-
- s->codec.tx_rate = rate->rate;
-}
-
-static void tsc2102_audio_output_update(TSC210xState *s)
-{
- int enable;
- struct audsettings fmt;
-
- if (s->dac_voice[0]) {
- tsc210x_out_flush(s, s->codec.out.len);
- s->codec.out.size = 0;
- AUD_set_active_out(s->dac_voice[0], 0);
- AUD_close_out(&s->card, s->dac_voice[0]);
- s->dac_voice[0] = NULL;
- }
- s->codec.cts = 0;
-
- enable =
- (~s->dac_power & (1 << 15)) && /* PWDNC */
- (~s->dac_power & (1 << 10)); /* DAPWDN */
- if (!enable || !s->codec.tx_rate)
- return;
-
- /* Force our own sampling rate even in slave DAC mode */
- fmt.endianness = 0;
- fmt.nchannels = 2;
- fmt.freq = s->codec.tx_rate;
- fmt.fmt = AUD_FMT_S16;
-
- s->dac_voice[0] = AUD_open_out(&s->card, s->dac_voice[0],
- "tsc2102.sink", s, (void *) tsc210x_audio_out_cb, &fmt);
- if (s->dac_voice[0]) {
- s->codec.cts = 1;
- AUD_set_active_out(s->dac_voice[0], 1);
- }
-}
-
-static uint16_t tsc2102_data_register_read(TSC210xState *s, int reg)
-{
- switch (reg) {
- case 0x00: /* X */
- s->dav &= 0xfbff;
- return TSC_CUT_RESOLUTION(X_TRANSFORM(s), s->precision) +
- (s->noise & 3);
-
- case 0x01: /* Y */
- s->noise ++;
- s->dav &= 0xfdff;
- return TSC_CUT_RESOLUTION(Y_TRANSFORM(s), s->precision) ^
- (s->noise & 3);
-
- case 0x02: /* Z1 */
- s->dav &= 0xfeff;
- return TSC_CUT_RESOLUTION(Z1_TRANSFORM(s), s->precision) -
- (s->noise & 3);
-
- case 0x03: /* Z2 */
- s->dav &= 0xff7f;
- return TSC_CUT_RESOLUTION(Z2_TRANSFORM(s), s->precision) |
- (s->noise & 3);
-
- case 0x04: /* KPData */
- if ((s->model & 0xff00) == 0x2300) {
- if (s->kb.intr && (s->kb.mode & 2)) {
- s->kb.intr = 0;
- qemu_irq_raise(s->kbint);
- }
- return s->kb.down;
- }
-
- return 0xffff;
-
- case 0x05: /* BAT1 */
- s->dav &= 0xffbf;
- return TSC_CUT_RESOLUTION(BAT1_VAL, s->precision) +
- (s->noise & 6);
-
- case 0x06: /* BAT2 */
- s->dav &= 0xffdf;
- return TSC_CUT_RESOLUTION(BAT2_VAL, s->precision);
-
- case 0x07: /* AUX1 */
- s->dav &= 0xffef;
- return TSC_CUT_RESOLUTION(AUX1_VAL, s->precision);
-
- case 0x08: /* AUX2 */
- s->dav &= 0xfff7;
- return 0xffff;
-
- case 0x09: /* TEMP1 */
- s->dav &= 0xfffb;
- return TSC_CUT_RESOLUTION(TEMP1_VAL, s->precision) -
- (s->noise & 5);
-
- case 0x0a: /* TEMP2 */
- s->dav &= 0xfffd;
- return TSC_CUT_RESOLUTION(TEMP2_VAL, s->precision) ^
- (s->noise & 3);
-
- case 0x0b: /* DAC */
- s->dav &= 0xfffe;
- return 0xffff;
-
- default:
-#ifdef TSC_VERBOSE
- fprintf(stderr, "tsc2102_data_register_read: "
- "no such register: 0x%02x\n", reg);
-#endif
- return 0xffff;
- }
-}
-
-static uint16_t tsc2102_control_register_read(
- TSC210xState *s, int reg)
-{
- switch (reg) {
- case 0x00: /* TSC ADC */
- return (s->pressure << 15) | ((!s->busy) << 14) |
- (s->nextfunction << 10) | (s->nextprecision << 8) | s->filter;
-
- case 0x01: /* Status / Keypad Control */
- if ((s->model & 0xff00) == 0x2100)
- return (s->pin_func << 14) | ((!s->enabled) << 13) |
- (s->host_mode << 12) | ((!!s->dav) << 11) | s->dav;
- else
- return (s->kb.intr << 15) | ((s->kb.scan || !s->kb.down) << 14) |
- (s->kb.debounce << 11);
-
- case 0x02: /* DAC Control */
- if ((s->model & 0xff00) == 0x2300)
- return s->dac_power & 0x8000;
- else
- goto bad_reg;
-
- case 0x03: /* Reference */
- return s->ref;
-
- case 0x04: /* Reset */
- return 0xffff;
-
- case 0x05: /* Configuration */
- return s->timing;
-
- case 0x06: /* Secondary configuration */
- if ((s->model & 0xff00) == 0x2100)
- goto bad_reg;
- return ((!s->dav) << 15) | ((s->kb.mode & 1) << 14) | s->pll[2];
-
- case 0x10: /* Keypad Mask */
- if ((s->model & 0xff00) == 0x2100)
- goto bad_reg;
- return s->kb.mask;
-
- default:
- bad_reg:
-#ifdef TSC_VERBOSE
- fprintf(stderr, "tsc2102_control_register_read: "
- "no such register: 0x%02x\n", reg);
-#endif
- return 0xffff;
- }
-}
-
-static uint16_t tsc2102_audio_register_read(TSC210xState *s, int reg)
-{
- int l_ch, r_ch;
- uint16_t val;
-
- switch (reg) {
- case 0x00: /* Audio Control 1 */
- return s->audio_ctrl1;
-
- case 0x01:
- return 0xff00;
-
- case 0x02: /* DAC Volume Control */
- return s->volume;
-
- case 0x03:
- return 0x8b00;
-
- case 0x04: /* Audio Control 2 */
- l_ch = 1;
- r_ch = 1;
- if (s->softstep && !(s->dac_power & (1 << 10))) {
- l_ch = (qemu_get_clock_ns(vm_clock) >
- s->volume_change + TSC_SOFTSTEP_DELAY);
- r_ch = (qemu_get_clock_ns(vm_clock) >
- s->volume_change + TSC_SOFTSTEP_DELAY);
- }
-
- return s->audio_ctrl2 | (l_ch << 3) | (r_ch << 2);
-
- case 0x05: /* Stereo DAC Power Control */
- return 0x2aa0 | s->dac_power |
- (((s->dac_power & (1 << 10)) &&
- (qemu_get_clock_ns(vm_clock) >
- s->powerdown + TSC_POWEROFF_DELAY)) << 6);
-
- case 0x06: /* Audio Control 3 */
- val = s->audio_ctrl3 | 0x0001;
- s->audio_ctrl3 &= 0xff3f;
- return val;
-
- case 0x07: /* LCH_BASS_BOOST_N0 */
- case 0x08: /* LCH_BASS_BOOST_N1 */
- case 0x09: /* LCH_BASS_BOOST_N2 */
- case 0x0a: /* LCH_BASS_BOOST_N3 */
- case 0x0b: /* LCH_BASS_BOOST_N4 */
- case 0x0c: /* LCH_BASS_BOOST_N5 */
- case 0x0d: /* LCH_BASS_BOOST_D1 */
- case 0x0e: /* LCH_BASS_BOOST_D2 */
- case 0x0f: /* LCH_BASS_BOOST_D4 */
- case 0x10: /* LCH_BASS_BOOST_D5 */
- case 0x11: /* RCH_BASS_BOOST_N0 */
- case 0x12: /* RCH_BASS_BOOST_N1 */
- case 0x13: /* RCH_BASS_BOOST_N2 */
- case 0x14: /* RCH_BASS_BOOST_N3 */
- case 0x15: /* RCH_BASS_BOOST_N4 */
- case 0x16: /* RCH_BASS_BOOST_N5 */
- case 0x17: /* RCH_BASS_BOOST_D1 */
- case 0x18: /* RCH_BASS_BOOST_D2 */
- case 0x19: /* RCH_BASS_BOOST_D4 */
- case 0x1a: /* RCH_BASS_BOOST_D5 */
- return s->filter_data[reg - 0x07];
-
- case 0x1b: /* PLL Programmability 1 */
- return s->pll[0];
-
- case 0x1c: /* PLL Programmability 2 */
- return s->pll[1];
-
- case 0x1d: /* Audio Control 4 */
- return (!s->softstep) << 14;
-
- default:
-#ifdef TSC_VERBOSE
- fprintf(stderr, "tsc2102_audio_register_read: "
- "no such register: 0x%02x\n", reg);
-#endif
- return 0xffff;
- }
-}
-
-static void tsc2102_data_register_write(
- TSC210xState *s, int reg, uint16_t value)
-{
- switch (reg) {
- case 0x00: /* X */
- case 0x01: /* Y */
- case 0x02: /* Z1 */
- case 0x03: /* Z2 */
- case 0x05: /* BAT1 */
- case 0x06: /* BAT2 */
- case 0x07: /* AUX1 */
- case 0x08: /* AUX2 */
- case 0x09: /* TEMP1 */
- case 0x0a: /* TEMP2 */
- return;
-
- default:
-#ifdef TSC_VERBOSE
- fprintf(stderr, "tsc2102_data_register_write: "
- "no such register: 0x%02x\n", reg);
-#endif
- }
-}
-
-static void tsc2102_control_register_write(
- TSC210xState *s, int reg, uint16_t value)
-{
- switch (reg) {
- case 0x00: /* TSC ADC */
- s->host_mode = value >> 15;
- s->enabled = !(value & 0x4000);
- if (s->busy && !s->enabled)
- qemu_del_timer(s->timer);
- s->busy &= s->enabled;
- s->nextfunction = (value >> 10) & 0xf;
- s->nextprecision = (value >> 8) & 3;
- s->filter = value & 0xff;
- return;
-
- case 0x01: /* Status / Keypad Control */
- if ((s->model & 0xff00) == 0x2100)
- s->pin_func = value >> 14;
- else {
- s->kb.scan = (value >> 14) & 1;
- s->kb.debounce = (value >> 11) & 7;
- if (s->kb.intr && s->kb.scan) {
- s->kb.intr = 0;
- qemu_irq_raise(s->kbint);
- }
- }
- return;
-
- case 0x02: /* DAC Control */
- if ((s->model & 0xff00) == 0x2300) {
- s->dac_power &= 0x7fff;
- s->dac_power |= 0x8000 & value;
- } else
- goto bad_reg;
- break;
-
- case 0x03: /* Reference */
- s->ref = value & 0x1f;
- return;
-
- case 0x04: /* Reset */
- if (value == 0xbb00) {
- if (s->busy)
- qemu_del_timer(s->timer);
- tsc210x_reset(s);
-#ifdef TSC_VERBOSE
- } else {
- fprintf(stderr, "tsc2102_control_register_write: "
- "wrong value written into RESET\n");
-#endif
- }
- return;
-
- case 0x05: /* Configuration */
- s->timing = value & 0x3f;
-#ifdef TSC_VERBOSE
- if (value & ~0x3f)
- fprintf(stderr, "tsc2102_control_register_write: "
- "wrong value written into CONFIG\n");
-#endif
- return;
-
- case 0x06: /* Secondary configuration */
- if ((s->model & 0xff00) == 0x2100)
- goto bad_reg;
- s->kb.mode = value >> 14;
- s->pll[2] = value & 0x3ffff;
- return;
-
- case 0x10: /* Keypad Mask */
- if ((s->model & 0xff00) == 0x2100)
- goto bad_reg;
- s->kb.mask = value;
- return;
-
- default:
- bad_reg:
-#ifdef TSC_VERBOSE
- fprintf(stderr, "tsc2102_control_register_write: "
- "no such register: 0x%02x\n", reg);
-#endif
- }
-}
-
-static void tsc2102_audio_register_write(
- TSC210xState *s, int reg, uint16_t value)
-{
- switch (reg) {
- case 0x00: /* Audio Control 1 */
- s->audio_ctrl1 = value & 0x0f3f;
-#ifdef TSC_VERBOSE
- if ((value & ~0x0f3f) || ((value & 7) != ((value >> 3) & 7)))
- fprintf(stderr, "tsc2102_audio_register_write: "
- "wrong value written into Audio 1\n");
-#endif
- tsc2102_audio_rate_update(s);
- tsc2102_audio_output_update(s);
- return;
-
- case 0x01:
-#ifdef TSC_VERBOSE
- if (value != 0xff00)
- fprintf(stderr, "tsc2102_audio_register_write: "
- "wrong value written into reg 0x01\n");
-#endif
- return;
-
- case 0x02: /* DAC Volume Control */
- s->volume = value;
- s->volume_change = qemu_get_clock_ns(vm_clock);
- return;
-
- case 0x03:
-#ifdef TSC_VERBOSE
- if (value != 0x8b00)
- fprintf(stderr, "tsc2102_audio_register_write: "
- "wrong value written into reg 0x03\n");
-#endif
- return;
-
- case 0x04: /* Audio Control 2 */
- s->audio_ctrl2 = value & 0xf7f2;
-#ifdef TSC_VERBOSE
- if (value & ~0xf7fd)
- fprintf(stderr, "tsc2102_audio_register_write: "
- "wrong value written into Audio 2\n");
-#endif
- return;
-
- case 0x05: /* Stereo DAC Power Control */
- if ((value & ~s->dac_power) & (1 << 10))
- s->powerdown = qemu_get_clock_ns(vm_clock);
-
- s->dac_power = value & 0x9543;
-#ifdef TSC_VERBOSE
- if ((value & ~0x9543) != 0x2aa0)
- fprintf(stderr, "tsc2102_audio_register_write: "
- "wrong value written into Power\n");
-#endif
- tsc2102_audio_rate_update(s);
- tsc2102_audio_output_update(s);
- return;
-
- case 0x06: /* Audio Control 3 */
- s->audio_ctrl3 &= 0x00c0;
- s->audio_ctrl3 |= value & 0xf800;
-#ifdef TSC_VERBOSE
- if (value & ~0xf8c7)
- fprintf(stderr, "tsc2102_audio_register_write: "
- "wrong value written into Audio 3\n");
-#endif
- tsc2102_audio_output_update(s);
- return;
-
- case 0x07: /* LCH_BASS_BOOST_N0 */
- case 0x08: /* LCH_BASS_BOOST_N1 */
- case 0x09: /* LCH_BASS_BOOST_N2 */
- case 0x0a: /* LCH_BASS_BOOST_N3 */
- case 0x0b: /* LCH_BASS_BOOST_N4 */
- case 0x0c: /* LCH_BASS_BOOST_N5 */
- case 0x0d: /* LCH_BASS_BOOST_D1 */
- case 0x0e: /* LCH_BASS_BOOST_D2 */
- case 0x0f: /* LCH_BASS_BOOST_D4 */
- case 0x10: /* LCH_BASS_BOOST_D5 */
- case 0x11: /* RCH_BASS_BOOST_N0 */
- case 0x12: /* RCH_BASS_BOOST_N1 */
- case 0x13: /* RCH_BASS_BOOST_N2 */
- case 0x14: /* RCH_BASS_BOOST_N3 */
- case 0x15: /* RCH_BASS_BOOST_N4 */
- case 0x16: /* RCH_BASS_BOOST_N5 */
- case 0x17: /* RCH_BASS_BOOST_D1 */
- case 0x18: /* RCH_BASS_BOOST_D2 */
- case 0x19: /* RCH_BASS_BOOST_D4 */
- case 0x1a: /* RCH_BASS_BOOST_D5 */
- s->filter_data[reg - 0x07] = value;
- return;
-
- case 0x1b: /* PLL Programmability 1 */
- s->pll[0] = value & 0xfffc;
-#ifdef TSC_VERBOSE
- if (value & ~0xfffc)
- fprintf(stderr, "tsc2102_audio_register_write: "
- "wrong value written into PLL 1\n");
-#endif
- return;
-
- case 0x1c: /* PLL Programmability 2 */
- s->pll[1] = value & 0xfffc;
-#ifdef TSC_VERBOSE
- if (value & ~0xfffc)
- fprintf(stderr, "tsc2102_audio_register_write: "
- "wrong value written into PLL 2\n");
-#endif
- return;
-
- case 0x1d: /* Audio Control 4 */
- s->softstep = !(value & 0x4000);
-#ifdef TSC_VERBOSE
- if (value & ~0x4000)
- fprintf(stderr, "tsc2102_audio_register_write: "
- "wrong value written into Audio 4\n");
-#endif
- return;
-
- default:
-#ifdef TSC_VERBOSE
- fprintf(stderr, "tsc2102_audio_register_write: "
- "no such register: 0x%02x\n", reg);
-#endif
- }
-}
-
-/* This handles most of the chip logic. */
-static void tsc210x_pin_update(TSC210xState *s)
-{
- int64_t expires;
- int pin_state;
-
- switch (s->pin_func) {
- case 0:
- pin_state = s->pressure;
- break;
- case 1:
- pin_state = !!s->dav;
- break;
- case 2:
- default:
- pin_state = s->pressure && !s->dav;
- }
-
- if (!s->enabled)
- pin_state = 0;
-
- if (pin_state != s->irq) {
- s->irq = pin_state;
- qemu_set_irq(s->pint, !s->irq);
- }
-
- switch (s->nextfunction) {
- case TSC_MODE_XY_SCAN:
- case TSC_MODE_XYZ_SCAN:
- if (!s->pressure)
- return;
- break;
-
- case TSC_MODE_X:
- case TSC_MODE_Y:
- case TSC_MODE_Z:
- if (!s->pressure)
- return;
- /* Fall through */
- case TSC_MODE_BAT1:
- case TSC_MODE_BAT2:
- case TSC_MODE_AUX:
- case TSC_MODE_TEMP1:
- case TSC_MODE_TEMP2:
- if (s->dav)
- s->enabled = 0;
- break;
-
- case TSC_MODE_AUX_SCAN:
- case TSC_MODE_PORT_SCAN:
- break;
-
- case TSC_MODE_NO_SCAN:
- case TSC_MODE_XX_DRV:
- case TSC_MODE_YY_DRV:
- case TSC_MODE_YX_DRV:
- default:
- return;
- }
-
- if (!s->enabled || s->busy || s->dav)
- return;
-
- s->busy = 1;
- s->precision = s->nextprecision;
- s->function = s->nextfunction;
- expires = qemu_get_clock_ns(vm_clock) + (get_ticks_per_sec() >> 10);
- qemu_mod_timer(s->timer, expires);
-}
-
-static uint16_t tsc210x_read(TSC210xState *s)
-{
- uint16_t ret = 0x0000;
-
- if (!s->command)
- fprintf(stderr, "tsc210x_read: SPI underrun!\n");
-
- switch (s->page) {
- case TSC_DATA_REGISTERS_PAGE:
- ret = tsc2102_data_register_read(s, s->offset);
- if (!s->dav)
- qemu_irq_raise(s->davint);
- break;
- case TSC_CONTROL_REGISTERS_PAGE:
- ret = tsc2102_control_register_read(s, s->offset);
- break;
- case TSC_AUDIO_REGISTERS_PAGE:
- ret = tsc2102_audio_register_read(s, s->offset);
- break;
- default:
- hw_error("tsc210x_read: wrong memory page\n");
- }
-
- tsc210x_pin_update(s);
-
- /* Allow sequential reads. */
- s->offset ++;
- s->state = 0;
- return ret;
-}
-
-static void tsc210x_write(TSC210xState *s, uint16_t value)
-{
- /*
- * This is a two-state state machine for reading
- * command and data every second time.
- */
- if (!s->state) {
- s->command = value >> 15;
- s->page = (value >> 11) & 0x0f;
- s->offset = (value >> 5) & 0x3f;
- s->state = 1;
- } else {
- if (s->command)
- fprintf(stderr, "tsc210x_write: SPI overrun!\n");
- else
- switch (s->page) {
- case TSC_DATA_REGISTERS_PAGE:
- tsc2102_data_register_write(s, s->offset, value);
- break;
- case TSC_CONTROL_REGISTERS_PAGE:
- tsc2102_control_register_write(s, s->offset, value);
- break;
- case TSC_AUDIO_REGISTERS_PAGE:
- tsc2102_audio_register_write(s, s->offset, value);
- break;
- default:
- hw_error("tsc210x_write: wrong memory page\n");
- }
-
- tsc210x_pin_update(s);
- s->state = 0;
- }
-}
-
-uint32_t tsc210x_txrx(void *opaque, uint32_t value, int len)
-{
- TSC210xState *s = opaque;
- uint32_t ret = 0;
-
- if (len != 16)
- hw_error("%s: FIXME: bad SPI word width %i\n", __FUNCTION__, len);
-
- /* TODO: sequential reads etc - how do we make sure the host doesn't
- * unintentionally read out a conversion result from a register while
- * transmitting the command word of the next command? */
- if (!value || (s->state && s->command))
- ret = tsc210x_read(s);
- if (value || (s->state && !s->command))
- tsc210x_write(s, value);
-
- return ret;
-}
-
-static void tsc210x_timer_tick(void *opaque)
-{
- TSC210xState *s = opaque;
-
- /* Timer ticked -- a set of conversions has been finished. */
-
- if (!s->busy)
- return;
-
- s->busy = 0;
- s->dav |= mode_regs[s->function];
- tsc210x_pin_update(s);
- qemu_irq_lower(s->davint);
-}
-
-static void tsc210x_touchscreen_event(void *opaque,
- int x, int y, int z, int buttons_state)
-{
- TSC210xState *s = opaque;
- int p = s->pressure;
-
- if (buttons_state) {
- s->x = x;
- s->y = y;
- }
- s->pressure = !!buttons_state;
-
- /*
- * Note: We would get better responsiveness in the guest by
- * signaling TS events immediately, but for now we simulate
- * the first conversion delay for sake of correctness.
- */
- if (p != s->pressure)
- tsc210x_pin_update(s);
-}
-
-static void tsc210x_i2s_swallow(TSC210xState *s)
-{
- if (s->dac_voice[0])
- tsc210x_out_flush(s, s->codec.out.len);
- else
- s->codec.out.len = 0;
-}
-
-static void tsc210x_i2s_set_rate(TSC210xState *s, int in, int out)
-{
- s->i2s_tx_rate = out;
- s->i2s_rx_rate = in;
-}
-
-static void tsc210x_save(QEMUFile *f, void *opaque)
-{
- TSC210xState *s = (TSC210xState *) opaque;
- int64_t now = qemu_get_clock_ns(vm_clock);
- int i;
-
- qemu_put_be16(f, s->x);
- qemu_put_be16(f, s->y);
- qemu_put_byte(f, s->pressure);
-
- qemu_put_byte(f, s->state);
- qemu_put_byte(f, s->page);
- qemu_put_byte(f, s->offset);
- qemu_put_byte(f, s->command);
-
- qemu_put_byte(f, s->irq);
- qemu_put_be16s(f, &s->dav);
-
- qemu_put_timer(f, s->timer);
- qemu_put_byte(f, s->enabled);
- qemu_put_byte(f, s->host_mode);
- qemu_put_byte(f, s->function);
- qemu_put_byte(f, s->nextfunction);
- qemu_put_byte(f, s->precision);
- qemu_put_byte(f, s->nextprecision);
- qemu_put_byte(f, s->filter);
- qemu_put_byte(f, s->pin_func);
- qemu_put_byte(f, s->ref);
- qemu_put_byte(f, s->timing);
- qemu_put_be32(f, s->noise);
-
- qemu_put_be16s(f, &s->audio_ctrl1);
- qemu_put_be16s(f, &s->audio_ctrl2);
- qemu_put_be16s(f, &s->audio_ctrl3);
- qemu_put_be16s(f, &s->pll[0]);
- qemu_put_be16s(f, &s->pll[1]);
- qemu_put_be16s(f, &s->volume);
- qemu_put_sbe64(f, (s->volume_change - now));
- qemu_put_sbe64(f, (s->powerdown - now));
- qemu_put_byte(f, s->softstep);
- qemu_put_be16s(f, &s->dac_power);
-
- for (i = 0; i < 0x14; i ++)
- qemu_put_be16s(f, &s->filter_data[i]);
-}
-
-static int tsc210x_load(QEMUFile *f, void *opaque, int version_id)
-{
- TSC210xState *s = (TSC210xState *) opaque;
- int64_t now = qemu_get_clock_ns(vm_clock);
- int i;
-
- s->x = qemu_get_be16(f);
- s->y = qemu_get_be16(f);
- s->pressure = qemu_get_byte(f);
-
- s->state = qemu_get_byte(f);
- s->page = qemu_get_byte(f);
- s->offset = qemu_get_byte(f);
- s->command = qemu_get_byte(f);
-
- s->irq = qemu_get_byte(f);
- qemu_get_be16s(f, &s->dav);
-
- qemu_get_timer(f, s->timer);
- s->enabled = qemu_get_byte(f);
- s->host_mode = qemu_get_byte(f);
- s->function = qemu_get_byte(f);
- s->nextfunction = qemu_get_byte(f);
- s->precision = qemu_get_byte(f);
- s->nextprecision = qemu_get_byte(f);
- s->filter = qemu_get_byte(f);
- s->pin_func = qemu_get_byte(f);
- s->ref = qemu_get_byte(f);
- s->timing = qemu_get_byte(f);
- s->noise = qemu_get_be32(f);
-
- qemu_get_be16s(f, &s->audio_ctrl1);
- qemu_get_be16s(f, &s->audio_ctrl2);
- qemu_get_be16s(f, &s->audio_ctrl3);
- qemu_get_be16s(f, &s->pll[0]);
- qemu_get_be16s(f, &s->pll[1]);
- qemu_get_be16s(f, &s->volume);
- s->volume_change = qemu_get_sbe64(f) + now;
- s->powerdown = qemu_get_sbe64(f) + now;
- s->softstep = qemu_get_byte(f);
- qemu_get_be16s(f, &s->dac_power);
-
- for (i = 0; i < 0x14; i ++)
- qemu_get_be16s(f, &s->filter_data[i]);
-
- s->busy = qemu_timer_pending(s->timer);
- qemu_set_irq(s->pint, !s->irq);
- qemu_set_irq(s->davint, !s->dav);
-
- return 0;
-}
-
-uWireSlave *tsc2102_init(qemu_irq pint)
-{
- TSC210xState *s;
-
- s = (TSC210xState *)
- g_malloc0(sizeof(TSC210xState));
- memset(s, 0, sizeof(TSC210xState));
- s->x = 160;
- s->y = 160;
- s->pressure = 0;
- s->precision = s->nextprecision = 0;
- s->timer = qemu_new_timer_ns(vm_clock, tsc210x_timer_tick, s);
- s->pint = pint;
- s->model = 0x2102;
- s->name = "tsc2102";
-
- s->tr[0] = 0;
- s->tr[1] = 1;
- s->tr[2] = 1;
- s->tr[3] = 0;
- s->tr[4] = 1;
- s->tr[5] = 0;
- s->tr[6] = 1;
- s->tr[7] = 0;
-
- s->chip.opaque = s;
- s->chip.send = (void *) tsc210x_write;
- s->chip.receive = (void *) tsc210x_read;
-
- s->codec.opaque = s;
- s->codec.tx_swallow = (void *) tsc210x_i2s_swallow;
- s->codec.set_rate = (void *) tsc210x_i2s_set_rate;
- s->codec.in.fifo = s->in_fifo;
- s->codec.out.fifo = s->out_fifo;
-
- tsc210x_reset(s);
-
- qemu_add_mouse_event_handler(tsc210x_touchscreen_event, s, 1,
- "QEMU TSC2102-driven Touchscreen");
-
- AUD_register_card(s->name, &s->card);
-
- qemu_register_reset((void *) tsc210x_reset, s);
- register_savevm(NULL, s->name, -1, 0,
- tsc210x_save, tsc210x_load, s);
-
- return &s->chip;
-}
-
-uWireSlave *tsc2301_init(qemu_irq penirq, qemu_irq kbirq, qemu_irq dav)
-{
- TSC210xState *s;
-
- s = (TSC210xState *)
- g_malloc0(sizeof(TSC210xState));
- memset(s, 0, sizeof(TSC210xState));
- s->x = 400;
- s->y = 240;
- s->pressure = 0;
- s->precision = s->nextprecision = 0;
- s->timer = qemu_new_timer_ns(vm_clock, tsc210x_timer_tick, s);
- s->pint = penirq;
- s->kbint = kbirq;
- s->davint = dav;
- s->model = 0x2301;
- s->name = "tsc2301";
-
- s->tr[0] = 0;
- s->tr[1] = 1;
- s->tr[2] = 1;
- s->tr[3] = 0;
- s->tr[4] = 1;
- s->tr[5] = 0;
- s->tr[6] = 1;
- s->tr[7] = 0;
-
- s->chip.opaque = s;
- s->chip.send = (void *) tsc210x_write;
- s->chip.receive = (void *) tsc210x_read;
-
- s->codec.opaque = s;
- s->codec.tx_swallow = (void *) tsc210x_i2s_swallow;
- s->codec.set_rate = (void *) tsc210x_i2s_set_rate;
- s->codec.in.fifo = s->in_fifo;
- s->codec.out.fifo = s->out_fifo;
-
- tsc210x_reset(s);
-
- qemu_add_mouse_event_handler(tsc210x_touchscreen_event, s, 1,
- "QEMU TSC2301-driven Touchscreen");
-
- AUD_register_card(s->name, &s->card);
-
- qemu_register_reset((void *) tsc210x_reset, s);
- register_savevm(NULL, s->name, -1, 0, tsc210x_save, tsc210x_load, s);
-
- return &s->chip;
-}
-
-I2SCodec *tsc210x_codec(uWireSlave *chip)
-{
- TSC210xState *s = (TSC210xState *) chip->opaque;
-
- return &s->codec;
-}
-
-/*
- * Use tslib generated calibration data to generate ADC input values
- * from the touchscreen. Assuming 12-bit precision was used during
- * tslib calibration.
- */
-void tsc210x_set_transform(uWireSlave *chip,
- MouseTransformInfo *info)
-{
- TSC210xState *s = (TSC210xState *) chip->opaque;
-#if 0
- int64_t ltr[8];
-
- ltr[0] = (int64_t) info->a[1] * info->y;
- ltr[1] = (int64_t) info->a[4] * info->x;
- ltr[2] = (int64_t) info->a[1] * info->a[3] -
- (int64_t) info->a[4] * info->a[0];
- ltr[3] = (int64_t) info->a[2] * info->a[4] -
- (int64_t) info->a[5] * info->a[1];
- ltr[4] = (int64_t) info->a[0] * info->y;
- ltr[5] = (int64_t) info->a[3] * info->x;
- ltr[6] = (int64_t) info->a[4] * info->a[0] -
- (int64_t) info->a[1] * info->a[3];
- ltr[7] = (int64_t) info->a[2] * info->a[3] -
- (int64_t) info->a[5] * info->a[0];
-
- /* Avoid integer overflow */
- s->tr[0] = ltr[0] >> 11;
- s->tr[1] = ltr[1] >> 11;
- s->tr[2] = muldiv64(ltr[2], 1, info->a[6]);
- s->tr[3] = muldiv64(ltr[3], 1 << 4, ltr[2]);
- s->tr[4] = ltr[4] >> 11;
- s->tr[5] = ltr[5] >> 11;
- s->tr[6] = muldiv64(ltr[6], 1, info->a[6]);
- s->tr[7] = muldiv64(ltr[7], 1 << 4, ltr[6]);
-#else
-
- /* This version assumes touchscreen X & Y axis are parallel or
- * perpendicular to LCD's X & Y axis in some way. */
- if (abs(info->a[0]) > abs(info->a[1])) {
- s->tr[0] = 0;
- s->tr[1] = -info->a[6] * info->x;
- s->tr[2] = info->a[0];
- s->tr[3] = -info->a[2] / info->a[0];
- s->tr[4] = info->a[6] * info->y;
- s->tr[5] = 0;
- s->tr[6] = info->a[4];
- s->tr[7] = -info->a[5] / info->a[4];
- } else {
- s->tr[0] = info->a[6] * info->y;
- s->tr[1] = 0;
- s->tr[2] = info->a[1];
- s->tr[3] = -info->a[2] / info->a[1];
- s->tr[4] = 0;
- s->tr[5] = -info->a[6] * info->x;
- s->tr[6] = info->a[3];
- s->tr[7] = -info->a[5] / info->a[3];
- }
-
- s->tr[0] >>= 11;
- s->tr[1] >>= 11;
- s->tr[3] <<= 4;
- s->tr[4] >>= 11;
- s->tr[5] >>= 11;
- s->tr[7] <<= 4;
-#endif
-}
-
-void tsc210x_key_event(uWireSlave *chip, int key, int down)
-{
- TSC210xState *s = (TSC210xState *) chip->opaque;
-
- if (down)
- s->kb.down |= 1 << key;
- else
- s->kb.down &= ~(1 << key);
-
- if (down && (s->kb.down & ~s->kb.mask) && !s->kb.intr) {
- s->kb.intr = 1;
- qemu_irq_lower(s->kbint);
- } else if (s->kb.intr && !(s->kb.down & ~s->kb.mask) &&
- !(s->kb.mode & 1)) {
- s->kb.intr = 0;
- qemu_irq_raise(s->kbint);
- }
-}
+++ /dev/null
-/*
- * Texas Instruments TUSB6010 emulation.
- * Based on reverse-engineering of a linux driver.
- *
- * Copyright (C) 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) version 3 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 "qemu-common.h"
-#include "qemu/timer.h"
-#include "hw/usb.h"
-#include "hw/arm/omap.h"
-#include "hw/irq.h"
-#include "hw/arm/devices.h"
-#include "hw/sysbus.h"
-
-typedef struct TUSBState {
- SysBusDevice busdev;
- MemoryRegion iomem[2];
- qemu_irq irq;
- MUSBState *musb;
- QEMUTimer *otg_timer;
- QEMUTimer *pwr_timer;
-
- int power;
- uint32_t scratch;
- uint16_t test_reset;
- uint32_t prcm_config;
- uint32_t prcm_mngmt;
- uint16_t otg_status;
- uint32_t dev_config;
- int host_mode;
- uint32_t intr;
- uint32_t intr_ok;
- uint32_t mask;
- uint32_t usbip_intr;
- uint32_t usbip_mask;
- uint32_t gpio_intr;
- uint32_t gpio_mask;
- uint32_t gpio_config;
- uint32_t dma_intr;
- uint32_t dma_mask;
- uint32_t dma_map;
- uint32_t dma_config;
- uint32_t ep0_config;
- uint32_t rx_config[15];
- uint32_t tx_config[15];
- uint32_t wkup_mask;
- uint32_t pullup[2];
- uint32_t control_config;
- uint32_t otg_timer_val;
-} TUSBState;
-
-#define TUSB_DEVCLOCK 60000000 /* 60 MHz */
-
-#define TUSB_VLYNQ_CTRL 0x004
-
-/* Mentor Graphics OTG core registers. */
-#define TUSB_BASE_OFFSET 0x400
-
-/* FIFO registers, 32-bit. */
-#define TUSB_FIFO_BASE 0x600
-
-/* Device System & Control registers, 32-bit. */
-#define TUSB_SYS_REG_BASE 0x800
-
-#define TUSB_DEV_CONF (TUSB_SYS_REG_BASE + 0x000)
-#define TUSB_DEV_CONF_USB_HOST_MODE (1 << 16)
-#define TUSB_DEV_CONF_PROD_TEST_MODE (1 << 15)
-#define TUSB_DEV_CONF_SOFT_ID (1 << 1)
-#define TUSB_DEV_CONF_ID_SEL (1 << 0)
-
-#define TUSB_PHY_OTG_CTRL_ENABLE (TUSB_SYS_REG_BASE + 0x004)
-#define TUSB_PHY_OTG_CTRL (TUSB_SYS_REG_BASE + 0x008)
-#define TUSB_PHY_OTG_CTRL_WRPROTECT (0xa5 << 24)
-#define TUSB_PHY_OTG_CTRL_O_ID_PULLUP (1 << 23)
-#define TUSB_PHY_OTG_CTRL_O_VBUS_DET_EN (1 << 19)
-#define TUSB_PHY_OTG_CTRL_O_SESS_END_EN (1 << 18)
-#define TUSB_PHY_OTG_CTRL_TESTM2 (1 << 17)
-#define TUSB_PHY_OTG_CTRL_TESTM1 (1 << 16)
-#define TUSB_PHY_OTG_CTRL_TESTM0 (1 << 15)
-#define TUSB_PHY_OTG_CTRL_TX_DATA2 (1 << 14)
-#define TUSB_PHY_OTG_CTRL_TX_GZ2 (1 << 13)
-#define TUSB_PHY_OTG_CTRL_TX_ENABLE2 (1 << 12)
-#define TUSB_PHY_OTG_CTRL_DM_PULLDOWN (1 << 11)
-#define TUSB_PHY_OTG_CTRL_DP_PULLDOWN (1 << 10)
-#define TUSB_PHY_OTG_CTRL_OSC_EN (1 << 9)
-#define TUSB_PHY_OTG_CTRL_PHYREF_CLK(v) (((v) & 3) << 7)
-#define TUSB_PHY_OTG_CTRL_PD (1 << 6)
-#define TUSB_PHY_OTG_CTRL_PLL_ON (1 << 5)
-#define TUSB_PHY_OTG_CTRL_EXT_RPU (1 << 4)
-#define TUSB_PHY_OTG_CTRL_PWR_GOOD (1 << 3)
-#define TUSB_PHY_OTG_CTRL_RESET (1 << 2)
-#define TUSB_PHY_OTG_CTRL_SUSPENDM (1 << 1)
-#define TUSB_PHY_OTG_CTRL_CLK_MODE (1 << 0)
-
-/* OTG status register */
-#define TUSB_DEV_OTG_STAT (TUSB_SYS_REG_BASE + 0x00c)
-#define TUSB_DEV_OTG_STAT_PWR_CLK_GOOD (1 << 8)
-#define TUSB_DEV_OTG_STAT_SESS_END (1 << 7)
-#define TUSB_DEV_OTG_STAT_SESS_VALID (1 << 6)
-#define TUSB_DEV_OTG_STAT_VBUS_VALID (1 << 5)
-#define TUSB_DEV_OTG_STAT_VBUS_SENSE (1 << 4)
-#define TUSB_DEV_OTG_STAT_ID_STATUS (1 << 3)
-#define TUSB_DEV_OTG_STAT_HOST_DISCON (1 << 2)
-#define TUSB_DEV_OTG_STAT_LINE_STATE (3 << 0)
-#define TUSB_DEV_OTG_STAT_DP_ENABLE (1 << 1)
-#define TUSB_DEV_OTG_STAT_DM_ENABLE (1 << 0)
-
-#define TUSB_DEV_OTG_TIMER (TUSB_SYS_REG_BASE + 0x010)
-#define TUSB_DEV_OTG_TIMER_ENABLE (1 << 31)
-#define TUSB_DEV_OTG_TIMER_VAL(v) ((v) & 0x07ffffff)
-#define TUSB_PRCM_REV (TUSB_SYS_REG_BASE + 0x014)
-
-/* PRCM configuration register */
-#define TUSB_PRCM_CONF (TUSB_SYS_REG_BASE + 0x018)
-#define TUSB_PRCM_CONF_SFW_CPEN (1 << 24)
-#define TUSB_PRCM_CONF_SYS_CLKSEL(v) (((v) & 3) << 16)
-
-/* PRCM management register */
-#define TUSB_PRCM_MNGMT (TUSB_SYS_REG_BASE + 0x01c)
-#define TUSB_PRCM_MNGMT_SRP_FIX_TMR(v) (((v) & 0xf) << 25)
-#define TUSB_PRCM_MNGMT_SRP_FIX_EN (1 << 24)
-#define TUSB_PRCM_MNGMT_VBUS_VAL_TMR(v) (((v) & 0xf) << 20)
-#define TUSB_PRCM_MNGMT_VBUS_VAL_FLT_EN (1 << 19)
-#define TUSB_PRCM_MNGMT_DFT_CLK_DIS (1 << 18)
-#define TUSB_PRCM_MNGMT_VLYNQ_CLK_DIS (1 << 17)
-#define TUSB_PRCM_MNGMT_OTG_SESS_END_EN (1 << 10)
-#define TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN (1 << 9)
-#define TUSB_PRCM_MNGMT_OTG_ID_PULLUP (1 << 8)
-#define TUSB_PRCM_MNGMT_15_SW_EN (1 << 4)
-#define TUSB_PRCM_MNGMT_33_SW_EN (1 << 3)
-#define TUSB_PRCM_MNGMT_5V_CPEN (1 << 2)
-#define TUSB_PRCM_MNGMT_PM_IDLE (1 << 1)
-#define TUSB_PRCM_MNGMT_DEV_IDLE (1 << 0)
-
-/* Wake-up source clear and mask registers */
-#define TUSB_PRCM_WAKEUP_SOURCE (TUSB_SYS_REG_BASE + 0x020)
-#define TUSB_PRCM_WAKEUP_CLEAR (TUSB_SYS_REG_BASE + 0x028)
-#define TUSB_PRCM_WAKEUP_MASK (TUSB_SYS_REG_BASE + 0x02c)
-#define TUSB_PRCM_WAKEUP_RESERVED_BITS (0xffffe << 13)
-#define TUSB_PRCM_WGPIO_7 (1 << 12)
-#define TUSB_PRCM_WGPIO_6 (1 << 11)
-#define TUSB_PRCM_WGPIO_5 (1 << 10)
-#define TUSB_PRCM_WGPIO_4 (1 << 9)
-#define TUSB_PRCM_WGPIO_3 (1 << 8)
-#define TUSB_PRCM_WGPIO_2 (1 << 7)
-#define TUSB_PRCM_WGPIO_1 (1 << 6)
-#define TUSB_PRCM_WGPIO_0 (1 << 5)
-#define TUSB_PRCM_WHOSTDISCON (1 << 4) /* Host disconnect */
-#define TUSB_PRCM_WBUS (1 << 3) /* USB bus resume */
-#define TUSB_PRCM_WNORCS (1 << 2) /* NOR chip select */
-#define TUSB_PRCM_WVBUS (1 << 1) /* OTG PHY VBUS */
-#define TUSB_PRCM_WID (1 << 0) /* OTG PHY ID detect */
-
-#define TUSB_PULLUP_1_CTRL (TUSB_SYS_REG_BASE + 0x030)
-#define TUSB_PULLUP_2_CTRL (TUSB_SYS_REG_BASE + 0x034)
-#define TUSB_INT_CTRL_REV (TUSB_SYS_REG_BASE + 0x038)
-#define TUSB_INT_CTRL_CONF (TUSB_SYS_REG_BASE + 0x03c)
-#define TUSB_USBIP_INT_SRC (TUSB_SYS_REG_BASE + 0x040)
-#define TUSB_USBIP_INT_SET (TUSB_SYS_REG_BASE + 0x044)
-#define TUSB_USBIP_INT_CLEAR (TUSB_SYS_REG_BASE + 0x048)
-#define TUSB_USBIP_INT_MASK (TUSB_SYS_REG_BASE + 0x04c)
-#define TUSB_DMA_INT_SRC (TUSB_SYS_REG_BASE + 0x050)
-#define TUSB_DMA_INT_SET (TUSB_SYS_REG_BASE + 0x054)
-#define TUSB_DMA_INT_CLEAR (TUSB_SYS_REG_BASE + 0x058)
-#define TUSB_DMA_INT_MASK (TUSB_SYS_REG_BASE + 0x05c)
-#define TUSB_GPIO_INT_SRC (TUSB_SYS_REG_BASE + 0x060)
-#define TUSB_GPIO_INT_SET (TUSB_SYS_REG_BASE + 0x064)
-#define TUSB_GPIO_INT_CLEAR (TUSB_SYS_REG_BASE + 0x068)
-#define TUSB_GPIO_INT_MASK (TUSB_SYS_REG_BASE + 0x06c)
-
-/* NOR flash interrupt source registers */
-#define TUSB_INT_SRC (TUSB_SYS_REG_BASE + 0x070)
-#define TUSB_INT_SRC_SET (TUSB_SYS_REG_BASE + 0x074)
-#define TUSB_INT_SRC_CLEAR (TUSB_SYS_REG_BASE + 0x078)
-#define TUSB_INT_MASK (TUSB_SYS_REG_BASE + 0x07c)
-#define TUSB_INT_SRC_TXRX_DMA_DONE (1 << 24)
-#define TUSB_INT_SRC_USB_IP_CORE (1 << 17)
-#define TUSB_INT_SRC_OTG_TIMEOUT (1 << 16)
-#define TUSB_INT_SRC_VBUS_SENSE_CHNG (1 << 15)
-#define TUSB_INT_SRC_ID_STATUS_CHNG (1 << 14)
-#define TUSB_INT_SRC_DEV_WAKEUP (1 << 13)
-#define TUSB_INT_SRC_DEV_READY (1 << 12)
-#define TUSB_INT_SRC_USB_IP_TX (1 << 9)
-#define TUSB_INT_SRC_USB_IP_RX (1 << 8)
-#define TUSB_INT_SRC_USB_IP_VBUS_ERR (1 << 7)
-#define TUSB_INT_SRC_USB_IP_VBUS_REQ (1 << 6)
-#define TUSB_INT_SRC_USB_IP_DISCON (1 << 5)
-#define TUSB_INT_SRC_USB_IP_CONN (1 << 4)
-#define TUSB_INT_SRC_USB_IP_SOF (1 << 3)
-#define TUSB_INT_SRC_USB_IP_RST_BABBLE (1 << 2)
-#define TUSB_INT_SRC_USB_IP_RESUME (1 << 1)
-#define TUSB_INT_SRC_USB_IP_SUSPEND (1 << 0)
-
-#define TUSB_GPIO_REV (TUSB_SYS_REG_BASE + 0x080)
-#define TUSB_GPIO_CONF (TUSB_SYS_REG_BASE + 0x084)
-#define TUSB_DMA_CTRL_REV (TUSB_SYS_REG_BASE + 0x100)
-#define TUSB_DMA_REQ_CONF (TUSB_SYS_REG_BASE + 0x104)
-#define TUSB_EP0_CONF (TUSB_SYS_REG_BASE + 0x108)
-#define TUSB_EP_IN_SIZE (TUSB_SYS_REG_BASE + 0x10c)
-#define TUSB_DMA_EP_MAP (TUSB_SYS_REG_BASE + 0x148)
-#define TUSB_EP_OUT_SIZE (TUSB_SYS_REG_BASE + 0x14c)
-#define TUSB_EP_MAX_PACKET_SIZE_OFFSET (TUSB_SYS_REG_BASE + 0x188)
-#define TUSB_SCRATCH_PAD (TUSB_SYS_REG_BASE + 0x1c4)
-#define TUSB_WAIT_COUNT (TUSB_SYS_REG_BASE + 0x1c8)
-#define TUSB_PROD_TEST_RESET (TUSB_SYS_REG_BASE + 0x1d8)
-
-#define TUSB_DIDR1_LO (TUSB_SYS_REG_BASE + 0x1f8)
-#define TUSB_DIDR1_HI (TUSB_SYS_REG_BASE + 0x1fc)
-
-/* Device System & Control register bitfields */
-#define TUSB_INT_CTRL_CONF_INT_RLCYC(v) (((v) & 0x7) << 18)
-#define TUSB_INT_CTRL_CONF_INT_POLARITY (1 << 17)
-#define TUSB_INT_CTRL_CONF_INT_MODE (1 << 16)
-#define TUSB_GPIO_CONF_DMAREQ(v) (((v) & 0x3f) << 24)
-#define TUSB_DMA_REQ_CONF_BURST_SIZE(v) (((v) & 3) << 26)
-#define TUSB_DMA_REQ_CONF_DMA_RQ_EN(v) (((v) & 0x3f) << 20)
-#define TUSB_DMA_REQ_CONF_DMA_RQ_ASR(v) (((v) & 0xf) << 16)
-#define TUSB_EP0_CONFIG_SW_EN (1 << 8)
-#define TUSB_EP0_CONFIG_DIR_TX (1 << 7)
-#define TUSB_EP0_CONFIG_XFR_SIZE(v) ((v) & 0x7f)
-#define TUSB_EP_CONFIG_SW_EN (1 << 31)
-#define TUSB_EP_CONFIG_XFR_SIZE(v) ((v) & 0x7fffffff)
-#define TUSB_PROD_TEST_RESET_VAL 0xa596
-
-static void tusb_intr_update(TUSBState *s)
-{
- if (s->control_config & TUSB_INT_CTRL_CONF_INT_POLARITY)
- qemu_set_irq(s->irq, s->intr & ~s->mask & s->intr_ok);
- else
- qemu_set_irq(s->irq, (!(s->intr & ~s->mask)) & s->intr_ok);
-}
-
-static void tusb_usbip_intr_update(TUSBState *s)
-{
- /* TX interrupt in the MUSB */
- if (s->usbip_intr & 0x0000ffff & ~s->usbip_mask)
- s->intr |= TUSB_INT_SRC_USB_IP_TX;
- else
- s->intr &= ~TUSB_INT_SRC_USB_IP_TX;
-
- /* RX interrupt in the MUSB */
- if (s->usbip_intr & 0xffff0000 & ~s->usbip_mask)
- s->intr |= TUSB_INT_SRC_USB_IP_RX;
- else
- s->intr &= ~TUSB_INT_SRC_USB_IP_RX;
-
- /* XXX: What about TUSB_INT_SRC_USB_IP_CORE? */
-
- tusb_intr_update(s);
-}
-
-static void tusb_dma_intr_update(TUSBState *s)
-{
- if (s->dma_intr & ~s->dma_mask)
- s->intr |= TUSB_INT_SRC_TXRX_DMA_DONE;
- else
- s->intr &= ~TUSB_INT_SRC_TXRX_DMA_DONE;
-
- tusb_intr_update(s);
-}
-
-static void tusb_gpio_intr_update(TUSBState *s)
-{
- /* TODO: How is this signalled? */
-}
-
-extern CPUReadMemoryFunc * const musb_read[];
-extern CPUWriteMemoryFunc * const musb_write[];
-
-static uint32_t tusb_async_readb(void *opaque, hwaddr addr)
-{
- TUSBState *s = (TUSBState *) opaque;
-
- switch (addr & 0xfff) {
- case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff):
- return musb_read[0](s->musb, addr & 0x1ff);
-
- case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff):
- return musb_read[0](s->musb, 0x20 + ((addr >> 3) & 0x3c));
- }
-
- printf("%s: unknown register at %03x\n",
- __FUNCTION__, (int) (addr & 0xfff));
- return 0;
-}
-
-static uint32_t tusb_async_readh(void *opaque, hwaddr addr)
-{
- TUSBState *s = (TUSBState *) opaque;
-
- switch (addr & 0xfff) {
- case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff):
- return musb_read[1](s->musb, addr & 0x1ff);
-
- case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff):
- return musb_read[1](s->musb, 0x20 + ((addr >> 3) & 0x3c));
- }
-
- printf("%s: unknown register at %03x\n",
- __FUNCTION__, (int) (addr & 0xfff));
- return 0;
-}
-
-static uint32_t tusb_async_readw(void *opaque, hwaddr addr)
-{
- TUSBState *s = (TUSBState *) opaque;
- int offset = addr & 0xfff;
- int epnum;
- uint32_t ret;
-
- switch (offset) {
- case TUSB_DEV_CONF:
- return s->dev_config;
-
- case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff):
- return musb_read[2](s->musb, offset & 0x1ff);
-
- case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff):
- return musb_read[2](s->musb, 0x20 + ((addr >> 3) & 0x3c));
-
- case TUSB_PHY_OTG_CTRL_ENABLE:
- case TUSB_PHY_OTG_CTRL:
- return 0x00; /* TODO */
-
- case TUSB_DEV_OTG_STAT:
- ret = s->otg_status;
-#if 0
- if (!(s->prcm_mngmt & TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN))
- ret &= ~TUSB_DEV_OTG_STAT_VBUS_VALID;
-#endif
- return ret;
- case TUSB_DEV_OTG_TIMER:
- return s->otg_timer_val;
-
- case TUSB_PRCM_REV:
- return 0x20;
- case TUSB_PRCM_CONF:
- return s->prcm_config;
- case TUSB_PRCM_MNGMT:
- return s->prcm_mngmt;
- case TUSB_PRCM_WAKEUP_SOURCE:
- case TUSB_PRCM_WAKEUP_CLEAR: /* TODO: What does this one return? */
- return 0x00000000;
- case TUSB_PRCM_WAKEUP_MASK:
- return s->wkup_mask;
-
- case TUSB_PULLUP_1_CTRL:
- return s->pullup[0];
- case TUSB_PULLUP_2_CTRL:
- return s->pullup[1];
-
- case TUSB_INT_CTRL_REV:
- return 0x20;
- case TUSB_INT_CTRL_CONF:
- return s->control_config;
-
- case TUSB_USBIP_INT_SRC:
- case TUSB_USBIP_INT_SET: /* TODO: What do these two return? */
- case TUSB_USBIP_INT_CLEAR:
- return s->usbip_intr;
- case TUSB_USBIP_INT_MASK:
- return s->usbip_mask;
-
- case TUSB_DMA_INT_SRC:
- case TUSB_DMA_INT_SET: /* TODO: What do these two return? */
- case TUSB_DMA_INT_CLEAR:
- return s->dma_intr;
- case TUSB_DMA_INT_MASK:
- return s->dma_mask;
-
- case TUSB_GPIO_INT_SRC: /* TODO: What do these two return? */
- case TUSB_GPIO_INT_SET:
- case TUSB_GPIO_INT_CLEAR:
- return s->gpio_intr;
- case TUSB_GPIO_INT_MASK:
- return s->gpio_mask;
-
- case TUSB_INT_SRC:
- case TUSB_INT_SRC_SET: /* TODO: What do these two return? */
- case TUSB_INT_SRC_CLEAR:
- return s->intr;
- case TUSB_INT_MASK:
- return s->mask;
-
- case TUSB_GPIO_REV:
- return 0x30;
- case TUSB_GPIO_CONF:
- return s->gpio_config;
-
- case TUSB_DMA_CTRL_REV:
- return 0x30;
- case TUSB_DMA_REQ_CONF:
- return s->dma_config;
- case TUSB_EP0_CONF:
- return s->ep0_config;
- case TUSB_EP_IN_SIZE ... (TUSB_EP_IN_SIZE + 0x3b):
- epnum = (offset - TUSB_EP_IN_SIZE) >> 2;
- return s->tx_config[epnum];
- case TUSB_DMA_EP_MAP:
- return s->dma_map;
- case TUSB_EP_OUT_SIZE ... (TUSB_EP_OUT_SIZE + 0x3b):
- epnum = (offset - TUSB_EP_OUT_SIZE) >> 2;
- return s->rx_config[epnum];
- case TUSB_EP_MAX_PACKET_SIZE_OFFSET ...
- (TUSB_EP_MAX_PACKET_SIZE_OFFSET + 0x3b):
- return 0x00000000; /* TODO */
- case TUSB_WAIT_COUNT:
- return 0x00; /* TODO */
-
- case TUSB_SCRATCH_PAD:
- return s->scratch;
-
- case TUSB_PROD_TEST_RESET:
- return s->test_reset;
-
- /* DIE IDs */
- case TUSB_DIDR1_LO:
- return 0xa9453c59;
- case TUSB_DIDR1_HI:
- return 0x54059adf;
- }
-
- printf("%s: unknown register at %03x\n", __FUNCTION__, offset);
- return 0;
-}
-
-static void tusb_async_writeb(void *opaque, hwaddr addr,
- uint32_t value)
-{
- TUSBState *s = (TUSBState *) opaque;
-
- switch (addr & 0xfff) {
- case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff):
- musb_write[0](s->musb, addr & 0x1ff, value);
- break;
-
- case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff):
- musb_write[0](s->musb, 0x20 + ((addr >> 3) & 0x3c), value);
- break;
-
- default:
- printf("%s: unknown register at %03x\n",
- __FUNCTION__, (int) (addr & 0xfff));
- return;
- }
-}
-
-static void tusb_async_writeh(void *opaque, hwaddr addr,
- uint32_t value)
-{
- TUSBState *s = (TUSBState *) opaque;
-
- switch (addr & 0xfff) {
- case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff):
- musb_write[1](s->musb, addr & 0x1ff, value);
- break;
-
- case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff):
- musb_write[1](s->musb, 0x20 + ((addr >> 3) & 0x3c), value);
- break;
-
- default:
- printf("%s: unknown register at %03x\n",
- __FUNCTION__, (int) (addr & 0xfff));
- return;
- }
-}
-
-static void tusb_async_writew(void *opaque, hwaddr addr,
- uint32_t value)
-{
- TUSBState *s = (TUSBState *) opaque;
- int offset = addr & 0xfff;
- int epnum;
-
- switch (offset) {
- case TUSB_VLYNQ_CTRL:
- break;
-
- case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff):
- musb_write[2](s->musb, offset & 0x1ff, value);
- break;
-
- case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff):
- musb_write[2](s->musb, 0x20 + ((addr >> 3) & 0x3c), value);
- break;
-
- case TUSB_DEV_CONF:
- s->dev_config = value;
- s->host_mode = (value & TUSB_DEV_CONF_USB_HOST_MODE);
- if (value & TUSB_DEV_CONF_PROD_TEST_MODE)
- hw_error("%s: Product Test mode not allowed\n", __FUNCTION__);
- break;
-
- case TUSB_PHY_OTG_CTRL_ENABLE:
- case TUSB_PHY_OTG_CTRL:
- return; /* TODO */
- case TUSB_DEV_OTG_TIMER:
- s->otg_timer_val = value;
- if (value & TUSB_DEV_OTG_TIMER_ENABLE)
- qemu_mod_timer(s->otg_timer, qemu_get_clock_ns(vm_clock) +
- muldiv64(TUSB_DEV_OTG_TIMER_VAL(value),
- get_ticks_per_sec(), TUSB_DEVCLOCK));
- else
- qemu_del_timer(s->otg_timer);
- break;
-
- case TUSB_PRCM_CONF:
- s->prcm_config = value;
- break;
- case TUSB_PRCM_MNGMT:
- s->prcm_mngmt = value;
- break;
- case TUSB_PRCM_WAKEUP_CLEAR:
- break;
- case TUSB_PRCM_WAKEUP_MASK:
- s->wkup_mask = value;
- break;
-
- case TUSB_PULLUP_1_CTRL:
- s->pullup[0] = value;
- break;
- case TUSB_PULLUP_2_CTRL:
- s->pullup[1] = value;
- break;
- case TUSB_INT_CTRL_CONF:
- s->control_config = value;
- tusb_intr_update(s);
- break;
-
- case TUSB_USBIP_INT_SET:
- s->usbip_intr |= value;
- tusb_usbip_intr_update(s);
- break;
- case TUSB_USBIP_INT_CLEAR:
- s->usbip_intr &= ~value;
- tusb_usbip_intr_update(s);
- musb_core_intr_clear(s->musb, ~value);
- break;
- case TUSB_USBIP_INT_MASK:
- s->usbip_mask = value;
- tusb_usbip_intr_update(s);
- break;
-
- case TUSB_DMA_INT_SET:
- s->dma_intr |= value;
- tusb_dma_intr_update(s);
- break;
- case TUSB_DMA_INT_CLEAR:
- s->dma_intr &= ~value;
- tusb_dma_intr_update(s);
- break;
- case TUSB_DMA_INT_MASK:
- s->dma_mask = value;
- tusb_dma_intr_update(s);
- break;
-
- case TUSB_GPIO_INT_SET:
- s->gpio_intr |= value;
- tusb_gpio_intr_update(s);
- break;
- case TUSB_GPIO_INT_CLEAR:
- s->gpio_intr &= ~value;
- tusb_gpio_intr_update(s);
- break;
- case TUSB_GPIO_INT_MASK:
- s->gpio_mask = value;
- tusb_gpio_intr_update(s);
- break;
-
- case TUSB_INT_SRC_SET:
- s->intr |= value;
- tusb_intr_update(s);
- break;
- case TUSB_INT_SRC_CLEAR:
- s->intr &= ~value;
- tusb_intr_update(s);
- break;
- case TUSB_INT_MASK:
- s->mask = value;
- tusb_intr_update(s);
- break;
-
- case TUSB_GPIO_CONF:
- s->gpio_config = value;
- break;
- case TUSB_DMA_REQ_CONF:
- s->dma_config = value;
- break;
- case TUSB_EP0_CONF:
- s->ep0_config = value & 0x1ff;
- musb_set_size(s->musb, 0, TUSB_EP0_CONFIG_XFR_SIZE(value),
- value & TUSB_EP0_CONFIG_DIR_TX);
- break;
- case TUSB_EP_IN_SIZE ... (TUSB_EP_IN_SIZE + 0x3b):
- epnum = (offset - TUSB_EP_IN_SIZE) >> 2;
- s->tx_config[epnum] = value;
- musb_set_size(s->musb, epnum + 1, TUSB_EP_CONFIG_XFR_SIZE(value), 1);
- break;
- case TUSB_DMA_EP_MAP:
- s->dma_map = value;
- break;
- case TUSB_EP_OUT_SIZE ... (TUSB_EP_OUT_SIZE + 0x3b):
- epnum = (offset - TUSB_EP_OUT_SIZE) >> 2;
- s->rx_config[epnum] = value;
- musb_set_size(s->musb, epnum + 1, TUSB_EP_CONFIG_XFR_SIZE(value), 0);
- break;
- case TUSB_EP_MAX_PACKET_SIZE_OFFSET ...
- (TUSB_EP_MAX_PACKET_SIZE_OFFSET + 0x3b):
- return; /* TODO */
- case TUSB_WAIT_COUNT:
- return; /* TODO */
-
- case TUSB_SCRATCH_PAD:
- s->scratch = value;
- break;
-
- case TUSB_PROD_TEST_RESET:
- s->test_reset = value;
- break;
-
- default:
- printf("%s: unknown register at %03x\n", __FUNCTION__, offset);
- return;
- }
-}
-
-static const MemoryRegionOps tusb_async_ops = {
- .old_mmio = {
- .read = { tusb_async_readb, tusb_async_readh, tusb_async_readw, },
- .write = { tusb_async_writeb, tusb_async_writeh, tusb_async_writew, },
- },
- .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static void tusb_otg_tick(void *opaque)
-{
- TUSBState *s = (TUSBState *) opaque;
-
- s->otg_timer_val = 0;
- s->intr |= TUSB_INT_SRC_OTG_TIMEOUT;
- tusb_intr_update(s);
-}
-
-static void tusb_power_tick(void *opaque)
-{
- TUSBState *s = (TUSBState *) opaque;
-
- if (s->power) {
- s->intr_ok = ~0;
- tusb_intr_update(s);
- }
-}
-
-static void tusb_musb_core_intr(void *opaque, int source, int level)
-{
- TUSBState *s = (TUSBState *) opaque;
- uint16_t otg_status = s->otg_status;
-
- switch (source) {
- case musb_set_vbus:
- if (level)
- otg_status |= TUSB_DEV_OTG_STAT_VBUS_VALID;
- else
- otg_status &= ~TUSB_DEV_OTG_STAT_VBUS_VALID;
-
- /* XXX: only if TUSB_PHY_OTG_CTRL_OTG_VBUS_DET_EN set? */
- /* XXX: only if TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN set? */
- if (s->otg_status != otg_status) {
- s->otg_status = otg_status;
- s->intr |= TUSB_INT_SRC_VBUS_SENSE_CHNG;
- tusb_intr_update(s);
- }
- break;
-
- case musb_set_session:
- /* XXX: only if TUSB_PHY_OTG_CTRL_OTG_SESS_END_EN set? */
- /* XXX: only if TUSB_PRCM_MNGMT_OTG_SESS_END_EN set? */
- if (level) {
- s->otg_status |= TUSB_DEV_OTG_STAT_SESS_VALID;
- s->otg_status &= ~TUSB_DEV_OTG_STAT_SESS_END;
- } else {
- s->otg_status &= ~TUSB_DEV_OTG_STAT_SESS_VALID;
- s->otg_status |= TUSB_DEV_OTG_STAT_SESS_END;
- }
-
- /* XXX: some IRQ or anything? */
- break;
-
- case musb_irq_tx:
- case musb_irq_rx:
- s->usbip_intr = musb_core_intr_get(s->musb);
- /* Fall through. */
- default:
- if (level)
- s->intr |= 1 << source;
- else
- s->intr &= ~(1 << source);
- tusb_intr_update(s);
- break;
- }
-}
-
-static void tusb6010_power(TUSBState *s, int on)
-{
- if (!on) {
- s->power = 0;
- } else if (!s->power && on) {
- s->power = 1;
- /* Pull the interrupt down after TUSB6010 comes up. */
- s->intr_ok = 0;
- tusb_intr_update(s);
- qemu_mod_timer(s->pwr_timer,
- qemu_get_clock_ns(vm_clock) + get_ticks_per_sec() / 2);
- }
-}
-
-static void tusb6010_irq(void *opaque, int source, int level)
-{
- if (source) {
- tusb_musb_core_intr(opaque, source - 1, level);
- } else {
- tusb6010_power(opaque, level);
- }
-}
-
-static void tusb6010_reset(DeviceState *dev)
-{
- TUSBState *s = FROM_SYSBUS(TUSBState, SYS_BUS_DEVICE(dev));
- int i;
-
- s->test_reset = TUSB_PROD_TEST_RESET_VAL;
- s->host_mode = 0;
- s->dev_config = 0;
- s->otg_status = 0; /* !TUSB_DEV_OTG_STAT_ID_STATUS means host mode */
- s->power = 0;
- s->mask = 0xffffffff;
- s->intr = 0x00000000;
- s->otg_timer_val = 0;
- s->scratch = 0;
- s->prcm_config = 0;
- s->prcm_mngmt = 0;
- s->intr_ok = 0;
- s->usbip_intr = 0;
- s->usbip_mask = 0;
- s->gpio_intr = 0;
- s->gpio_mask = 0;
- s->gpio_config = 0;
- s->dma_intr = 0;
- s->dma_mask = 0;
- s->dma_map = 0;
- s->dma_config = 0;
- s->ep0_config = 0;
- s->wkup_mask = 0;
- s->pullup[0] = s->pullup[1] = 0;
- s->control_config = 0;
- for (i = 0; i < 15; i++) {
- s->rx_config[i] = s->tx_config[i] = 0;
- }
- musb_reset(s->musb);
-}
-
-static int tusb6010_init(SysBusDevice *dev)
-{
- TUSBState *s = FROM_SYSBUS(TUSBState, dev);
- s->otg_timer = qemu_new_timer_ns(vm_clock, tusb_otg_tick, s);
- s->pwr_timer = qemu_new_timer_ns(vm_clock, tusb_power_tick, s);
- memory_region_init_io(&s->iomem[1], &tusb_async_ops, s, "tusb-async",
- UINT32_MAX);
- sysbus_init_mmio(dev, &s->iomem[0]);
- sysbus_init_mmio(dev, &s->iomem[1]);
- sysbus_init_irq(dev, &s->irq);
- qdev_init_gpio_in(&dev->qdev, tusb6010_irq, musb_irq_max + 1);
- s->musb = musb_init(&dev->qdev, 1);
- return 0;
-}
-
-static void tusb6010_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-
- k->init = tusb6010_init;
- dc->reset = tusb6010_reset;
-}
-
-static const TypeInfo tusb6010_info = {
- .name = "tusb6010",
- .parent = TYPE_SYS_BUS_DEVICE,
- .instance_size = sizeof(TUSBState),
- .class_init = tusb6010_class_init,
-};
-
-static void tusb6010_register_types(void)
-{
- type_register_static(&tusb6010_info);
-}
-
-type_init(tusb6010_register_types)
#define SUN4M_H
#include "qemu-common.h"
+#include "exec/hwaddr.h"
+#include "qapi/qmp/types.h"
/* Devices used by sparc32 system. */