Commit 388f60b1cff919c2627e6cb5c0cbbeeb42130e19

Authored by Edgar E. Iglesias
1 parent 17628bc6

xilinx: Add OPB timer.

Signed-off-by: Edgar E. Iglesias <edgar.iglesias@gmail.com>
Showing 1 changed file with 224 additions and 0 deletions
hw/xilinx_timer.c 0 → 100644
  1 +/*
  2 + * QEMU model of the Xilinx timer block.
  3 + *
  4 + * Copyright (c) 2009 Edgar E. Iglesias.
  5 + *
  6 + * Permission is hereby granted, free of charge, to any person obtaining a copy
  7 + * of this software and associated documentation files (the "Software"), to deal
  8 + * in the Software without restriction, including without limitation the rights
  9 + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 + * copies of the Software, and to permit persons to whom the Software is
  11 + * furnished to do so, subject to the following conditions:
  12 + *
  13 + * The above copyright notice and this permission notice shall be included in
  14 + * all copies or substantial portions of the Software.
  15 + *
  16 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  19 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 + * THE SOFTWARE.
  23 + */
  24 +
  25 +#include "sysbus.h"
  26 +#include "sysemu.h"
  27 +#include "qemu-timer.h"
  28 +
  29 +#define D(x)
  30 +
  31 +#define R_TCSR 0
  32 +#define R_TLR 1
  33 +#define R_TCR 2
  34 +#define R_MAX 4
  35 +
  36 +#define TCSR_MDT (1<<0)
  37 +#define TCSR_UDT (1<<1)
  38 +#define TCSR_GENT (1<<2)
  39 +#define TCSR_CAPT (1<<3)
  40 +#define TCSR_ARHT (1<<4)
  41 +#define TCSR_LOAD (1<<5)
  42 +#define TCSR_ENIT (1<<6)
  43 +#define TCSR_ENT (1<<7)
  44 +#define TCSR_TINT (1<<8)
  45 +#define TCSR_PWMA (1<<9)
  46 +#define TCSR_ENALL (1<<10)
  47 +
  48 +struct xlx_timer
  49 +{
  50 + QEMUBH *bh;
  51 + ptimer_state *ptimer;
  52 + void *parent;
  53 + int nr; /* for debug. */
  54 +
  55 + unsigned long timer_div;
  56 +
  57 + uint32_t regs[R_MAX];
  58 +};
  59 +
  60 +struct timerblock
  61 +{
  62 + SysBusDevice busdev;
  63 + qemu_irq irq;
  64 + unsigned int nr_timers;
  65 + struct xlx_timer *timers;
  66 +};
  67 +
  68 +static inline unsigned int timer_from_addr(target_phys_addr_t addr)
  69 +{
  70 + /* Timers get a 4x32bit control reg area each. */
  71 + return addr >> 2;
  72 +}
  73 +
  74 +static void timer_update_irq(struct timerblock *t)
  75 +{
  76 + unsigned int i, irq = 0;
  77 + uint32_t csr;
  78 +
  79 + for (i = 0; i < t->nr_timers; i++) {
  80 + csr = t->timers[i].regs[R_TCSR];
  81 + irq |= (csr & TCSR_TINT) && (csr & TCSR_ENIT);
  82 + }
  83 +
  84 + /* All timers within the same slave share a single IRQ line. */
  85 + qemu_set_irq(t->irq, !!irq);
  86 +}
  87 +
  88 +static uint32_t timer_readl (void *opaque, target_phys_addr_t addr)
  89 +{
  90 + struct timerblock *t = opaque;
  91 + struct xlx_timer *xt;
  92 + uint32_t r = 0;
  93 + unsigned int timer;
  94 +
  95 + addr >>= 2;
  96 + timer = timer_from_addr(addr);
  97 + xt = &t->timers[timer];
  98 + /* Further decoding to address a specific timers reg. */
  99 + addr &= 0x3;
  100 + switch (addr)
  101 + {
  102 + case R_TCR:
  103 + r = ptimer_get_count(xt->ptimer);
  104 + if (!(xt->regs[R_TCSR] & TCSR_UDT))
  105 + r = ~r;
  106 + D(qemu_log("xlx_timer t=%d read counter=%x udt=%d\n",
  107 + timer, r, xt->regs[R_TCSR] & TCSR_UDT));
  108 + break;
  109 + default:
  110 + if (addr < ARRAY_SIZE(xt->regs))
  111 + r = xt->regs[addr];
  112 + break;
  113 +
  114 + }
  115 + D(printf("%s timer=%d %x=%x\n", __func__, timer, addr * 4, r));
  116 + return r;
  117 +}
  118 +
  119 +static void timer_enable(struct xlx_timer *xt)
  120 +{
  121 + uint64_t count;
  122 +
  123 + D(printf("%s timer=%d down=%d\n", __func__,
  124 + xt->nr, xt->regs[R_TCSR] & TCSR_UDT));
  125 +
  126 + ptimer_stop(xt->ptimer);
  127 +
  128 + if (xt->regs[R_TCSR] & TCSR_UDT)
  129 + count = xt->regs[R_TLR];
  130 + else
  131 + count = ~0 - xt->regs[R_TLR];
  132 + ptimer_set_count(xt->ptimer, count);
  133 + ptimer_run(xt->ptimer, 1);
  134 +}
  135 +
  136 +static void
  137 +timer_writel (void *opaque, target_phys_addr_t addr, uint32_t value)
  138 +{
  139 + struct timerblock *t = opaque;
  140 + struct xlx_timer *xt;
  141 + unsigned int timer;
  142 +
  143 + addr >>= 2;
  144 + timer = timer_from_addr(addr);
  145 + xt = &t->timers[timer];
  146 + D(printf("%s addr=%x val=%x (timer=%d off=%d)\n",
  147 + __func__, addr * 4, value, timer, addr & 3));
  148 + /* Further decoding to address a specific timers reg. */
  149 + addr &= 3;
  150 + switch (addr)
  151 + {
  152 + case R_TCSR:
  153 + if (value & TCSR_TINT)
  154 + value &= ~TCSR_TINT;
  155 +
  156 + xt->regs[addr] = value;
  157 + if (value & TCSR_ENT)
  158 + timer_enable(xt);
  159 + break;
  160 +
  161 + default:
  162 + if (addr < ARRAY_SIZE(xt->regs))
  163 + xt->regs[addr] = value;
  164 + break;
  165 + }
  166 + timer_update_irq(t);
  167 +}
  168 +
  169 +static CPUReadMemoryFunc *timer_read[] = {
  170 + NULL, NULL,
  171 + &timer_readl,
  172 +};
  173 +
  174 +static CPUWriteMemoryFunc *timer_write[] = {
  175 + NULL, NULL,
  176 + &timer_writel,
  177 +};
  178 +
  179 +static void timer_hit(void *opaque)
  180 +{
  181 + struct xlx_timer *xt = opaque;
  182 + struct timerblock *t = xt->parent;
  183 + D(printf("%s %d\n", __func__, timer));
  184 + xt->regs[R_TCSR] |= TCSR_TINT;
  185 +
  186 + if (xt->regs[R_TCSR] & TCSR_ARHT)
  187 + timer_enable(xt);
  188 + timer_update_irq(t);
  189 +}
  190 +
  191 +static void xilinx_timer_init(SysBusDevice *dev)
  192 +{
  193 + struct timerblock *t = FROM_SYSBUS(typeof (*t), dev);
  194 + unsigned int i;
  195 + int timer_regs, freq_hz;
  196 +
  197 + /* All timers share a single irq line. */
  198 + sysbus_init_irq(dev, &t->irq);
  199 +
  200 + /* Init all the ptimers. */
  201 + freq_hz = qdev_get_prop_int(&dev->qdev, "frequency", 2);
  202 + t->nr_timers = qdev_get_prop_int(&dev->qdev, "nr-timers", 2);
  203 + t->timers = qemu_mallocz(sizeof t->timers[0] * t->nr_timers);
  204 + for (i = 0; i < t->nr_timers; i++) {
  205 + struct xlx_timer *xt = &t->timers[i];
  206 +
  207 + xt->parent = t;
  208 + xt->nr = i;
  209 + xt->bh = qemu_bh_new(timer_hit, xt);
  210 + xt->ptimer = ptimer_init(xt->bh);
  211 + ptimer_set_freq(xt->ptimer, freq_hz);
  212 + }
  213 +
  214 + timer_regs = cpu_register_io_memory(0, timer_read, timer_write, t);
  215 + sysbus_init_mmio(dev, R_MAX * 4 * t->nr_timers, timer_regs);
  216 +}
  217 +
  218 +static void xilinx_timer_register(void)
  219 +{
  220 + sysbus_register_dev("xilinx,timer", sizeof (struct timerblock),
  221 + xilinx_timer_init);
  222 +}
  223 +
  224 +device_init(xilinx_timer_register)
... ...