qemu/hw/timer/omap_gptimer.c
<<
>>
Prefs
   1/*
   2 * TI OMAP2 general purpose timers emulation.
   3 *
   4 * Copyright (C) 2007-2008 Nokia Corporation
   5 * Written by Andrzej Zaborowski <andrew@openedhand.com>
   6 *
   7 * This program is free software; you can redistribute it and/or
   8 * modify it under the terms of the GNU General Public License as
   9 * published by the Free Software Foundation; either version 2 or
  10 * (at your option) any later version of the License.
  11 *
  12 * This program is distributed in the hope that it will be useful,
  13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15 * GNU General Public License for more details.
  16 *
  17 * You should have received a copy of the GNU General Public License along
  18 * with this program; if not, see <http://www.gnu.org/licenses/>.
  19 */
  20#include "qemu/osdep.h"
  21#include "hw/hw.h"
  22#include "qemu/timer.h"
  23#include "hw/arm/omap.h"
  24
  25/* GP timers */
  26struct omap_gp_timer_s {
  27    MemoryRegion iomem;
  28    qemu_irq irq;
  29    qemu_irq wkup;
  30    qemu_irq in;
  31    qemu_irq out;
  32    omap_clk clk;
  33    QEMUTimer *timer;
  34    QEMUTimer *match;
  35    struct omap_target_agent_s *ta;
  36
  37    int in_val;
  38    int out_val;
  39    int64_t time;
  40    int64_t rate;
  41    int64_t ticks_per_sec;
  42
  43    int16_t config;
  44    int status;
  45    int it_ena;
  46    int wu_ena;
  47    int enable;
  48    int inout;
  49    int capt2;
  50    int pt;
  51    enum {
  52        gpt_trigger_none, gpt_trigger_overflow, gpt_trigger_both
  53    } trigger;
  54    enum {
  55        gpt_capture_none, gpt_capture_rising,
  56        gpt_capture_falling, gpt_capture_both
  57    } capture;
  58    int scpwm;
  59    int ce;
  60    int pre;
  61    int ptv;
  62    int ar;
  63    int st;
  64    int posted;
  65    uint32_t val;
  66    uint32_t load_val;
  67    uint32_t capture_val[2];
  68    uint32_t match_val;
  69    int capt_num;
  70
  71    uint16_t writeh;    /* LSB */
  72    uint16_t readh;     /* MSB */
  73};
  74
  75#define GPT_TCAR_IT     (1 << 2)
  76#define GPT_OVF_IT      (1 << 1)
  77#define GPT_MAT_IT      (1 << 0)
  78
  79static inline void omap_gp_timer_intr(struct omap_gp_timer_s *timer, int it)
  80{
  81    if (timer->it_ena & it) {
  82        if (!timer->status)
  83            qemu_irq_raise(timer->irq);
  84
  85        timer->status |= it;
  86        /* Or are the status bits set even when masked?
  87         * i.e. is masking applied before or after the status register?  */
  88    }
  89
  90    if (timer->wu_ena & it)
  91        qemu_irq_pulse(timer->wkup);
  92}
  93
  94static inline void omap_gp_timer_out(struct omap_gp_timer_s *timer, int level)
  95{
  96    if (!timer->inout && timer->out_val != level) {
  97        timer->out_val = level;
  98        qemu_set_irq(timer->out, level);
  99    }
 100}
 101
 102static inline uint32_t omap_gp_timer_read(struct omap_gp_timer_s *timer)
 103{
 104    uint64_t distance;
 105
 106    if (timer->st && timer->rate) {
 107        distance = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - timer->time;
 108        distance = muldiv64(distance, timer->rate, timer->ticks_per_sec);
 109
 110        if (distance >= 0xffffffff - timer->val)
 111            return 0xffffffff;
 112        else
 113            return timer->val + distance;
 114    } else
 115        return timer->val;
 116}
 117
 118static inline void omap_gp_timer_sync(struct omap_gp_timer_s *timer)
 119{
 120    if (timer->st) {
 121        timer->val = omap_gp_timer_read(timer);
 122        timer->time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
 123    }
 124}
 125
 126static inline void omap_gp_timer_update(struct omap_gp_timer_s *timer)
 127{
 128    int64_t expires, matches;
 129
 130    if (timer->st && timer->rate) {
 131        expires = muldiv64(0x100000000ll - timer->val,
 132                        timer->ticks_per_sec, timer->rate);
 133        timer_mod(timer->timer, timer->time + expires);
 134
 135        if (timer->ce && timer->match_val >= timer->val) {
 136            matches = muldiv64(timer->match_val - timer->val,
 137                            timer->ticks_per_sec, timer->rate);
 138            timer_mod(timer->match, timer->time + matches);
 139        } else
 140            timer_del(timer->match);
 141    } else {
 142        timer_del(timer->timer);
 143        timer_del(timer->match);
 144        omap_gp_timer_out(timer, timer->scpwm);
 145    }
 146}
 147
 148static inline void omap_gp_timer_trigger(struct omap_gp_timer_s *timer)
 149{
 150    if (timer->pt)
 151        /* TODO in overflow-and-match mode if the first event to
 152         * occur is the match, don't toggle.  */
 153        omap_gp_timer_out(timer, !timer->out_val);
 154    else
 155        /* TODO inverted pulse on timer->out_val == 1?  */
 156        qemu_irq_pulse(timer->out);
 157}
 158
 159static void omap_gp_timer_tick(void *opaque)
 160{
 161    struct omap_gp_timer_s *timer = (struct omap_gp_timer_s *) opaque;
 162
 163    if (!timer->ar) {
 164        timer->st = 0;
 165        timer->val = 0;
 166    } else {
 167        timer->val = timer->load_val;
 168        timer->time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
 169    }
 170
 171    if (timer->trigger == gpt_trigger_overflow ||
 172                    timer->trigger == gpt_trigger_both)
 173        omap_gp_timer_trigger(timer);
 174
 175    omap_gp_timer_intr(timer, GPT_OVF_IT);
 176    omap_gp_timer_update(timer);
 177}
 178
 179static void omap_gp_timer_match(void *opaque)
 180{
 181    struct omap_gp_timer_s *timer = (struct omap_gp_timer_s *) opaque;
 182
 183    if (timer->trigger == gpt_trigger_both)
 184        omap_gp_timer_trigger(timer);
 185
 186    omap_gp_timer_intr(timer, GPT_MAT_IT);
 187}
 188
 189static void omap_gp_timer_input(void *opaque, int line, int on)
 190{
 191    struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque;
 192    int trigger;
 193
 194    switch (s->capture) {
 195    default:
 196    case gpt_capture_none:
 197        trigger = 0;
 198        break;
 199    case gpt_capture_rising:
 200        trigger = !s->in_val && on;
 201        break;
 202    case gpt_capture_falling:
 203        trigger = s->in_val && !on;
 204        break;
 205    case gpt_capture_both:
 206        trigger = (s->in_val == !on);
 207        break;
 208    }
 209    s->in_val = on;
 210
 211    if (s->inout && trigger && s->capt_num < 2) {
 212        s->capture_val[s->capt_num] = omap_gp_timer_read(s);
 213
 214        if (s->capt2 == s->capt_num ++)
 215            omap_gp_timer_intr(s, GPT_TCAR_IT);
 216    }
 217}
 218
 219static void omap_gp_timer_clk_update(void *opaque, int line, int on)
 220{
 221    struct omap_gp_timer_s *timer = (struct omap_gp_timer_s *) opaque;
 222
 223    omap_gp_timer_sync(timer);
 224    timer->rate = on ? omap_clk_getrate(timer->clk) : 0;
 225    omap_gp_timer_update(timer);
 226}
 227
 228static void omap_gp_timer_clk_setup(struct omap_gp_timer_s *timer)
 229{
 230    omap_clk_adduser(timer->clk,
 231                     qemu_allocate_irq(omap_gp_timer_clk_update, timer, 0));
 232    timer->rate = omap_clk_getrate(timer->clk);
 233}
 234
 235void omap_gp_timer_reset(struct omap_gp_timer_s *s)
 236{
 237    s->config = 0x000;
 238    s->status = 0;
 239    s->it_ena = 0;
 240    s->wu_ena = 0;
 241    s->inout = 0;
 242    s->capt2 = 0;
 243    s->capt_num = 0;
 244    s->pt = 0;
 245    s->trigger = gpt_trigger_none;
 246    s->capture = gpt_capture_none;
 247    s->scpwm = 0;
 248    s->ce = 0;
 249    s->pre = 0;
 250    s->ptv = 0;
 251    s->ar = 0;
 252    s->st = 0;
 253    s->posted = 1;
 254    s->val = 0x00000000;
 255    s->load_val = 0x00000000;
 256    s->capture_val[0] = 0x00000000;
 257    s->capture_val[1] = 0x00000000;
 258    s->match_val = 0x00000000;
 259    omap_gp_timer_update(s);
 260}
 261
 262static uint32_t omap_gp_timer_readw(void *opaque, hwaddr addr)
 263{
 264    struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque;
 265
 266    switch (addr) {
 267    case 0x00:  /* TIDR */
 268        return 0x21;
 269
 270    case 0x10:  /* TIOCP_CFG */
 271        return s->config;
 272
 273    case 0x14:  /* TISTAT */
 274        /* ??? When's this bit reset? */
 275        return 1;                                               /* RESETDONE */
 276
 277    case 0x18:  /* TISR */
 278        return s->status;
 279
 280    case 0x1c:  /* TIER */
 281        return s->it_ena;
 282
 283    case 0x20:  /* TWER */
 284        return s->wu_ena;
 285
 286    case 0x24:  /* TCLR */
 287        return (s->inout << 14) |
 288                (s->capt2 << 13) |
 289                (s->pt << 12) |
 290                (s->trigger << 10) |
 291                (s->capture << 8) |
 292                (s->scpwm << 7) |
 293                (s->ce << 6) |
 294                (s->pre << 5) |
 295                (s->ptv << 2) |
 296                (s->ar << 1) |
 297                (s->st << 0);
 298
 299    case 0x28:  /* TCRR */
 300        return omap_gp_timer_read(s);
 301
 302    case 0x2c:  /* TLDR */
 303        return s->load_val;
 304
 305    case 0x30:  /* TTGR */
 306        return 0xffffffff;
 307
 308    case 0x34:  /* TWPS */
 309        return 0x00000000;      /* No posted writes pending.  */
 310
 311    case 0x38:  /* TMAR */
 312        return s->match_val;
 313
 314    case 0x3c:  /* TCAR1 */
 315        return s->capture_val[0];
 316
 317    case 0x40:  /* TSICR */
 318        return s->posted << 2;
 319
 320    case 0x44:  /* TCAR2 */
 321        return s->capture_val[1];
 322    }
 323
 324    OMAP_BAD_REG(addr);
 325    return 0;
 326}
 327
 328static uint32_t omap_gp_timer_readh(void *opaque, hwaddr addr)
 329{
 330    struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque;
 331    uint32_t ret;
 332
 333    if (addr & 2)
 334        return s->readh;
 335    else {
 336        ret = omap_gp_timer_readw(opaque, addr);
 337        s->readh = ret >> 16;
 338        return ret & 0xffff;
 339    }
 340}
 341
 342static void omap_gp_timer_write(void *opaque, hwaddr addr,
 343                uint32_t value)
 344{
 345    struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque;
 346
 347    switch (addr) {
 348    case 0x00:  /* TIDR */
 349    case 0x14:  /* TISTAT */
 350    case 0x34:  /* TWPS */
 351    case 0x3c:  /* TCAR1 */
 352    case 0x44:  /* TCAR2 */
 353        OMAP_RO_REG(addr);
 354        break;
 355
 356    case 0x10:  /* TIOCP_CFG */
 357        s->config = value & 0x33d;
 358        if (((value >> 3) & 3) == 3)                            /* IDLEMODE */
 359            fprintf(stderr, "%s: illegal IDLEMODE value in TIOCP_CFG\n",
 360                            __FUNCTION__);
 361        if (value & 2)                                          /* SOFTRESET */
 362            omap_gp_timer_reset(s);
 363        break;
 364
 365    case 0x18:  /* TISR */
 366        if (value & GPT_TCAR_IT)
 367            s->capt_num = 0;
 368        if (s->status && !(s->status &= ~value))
 369            qemu_irq_lower(s->irq);
 370        break;
 371
 372    case 0x1c:  /* TIER */
 373        s->it_ena = value & 7;
 374        break;
 375
 376    case 0x20:  /* TWER */
 377        s->wu_ena = value & 7;
 378        break;
 379
 380    case 0x24:  /* TCLR */
 381        omap_gp_timer_sync(s);
 382        s->inout = (value >> 14) & 1;
 383        s->capt2 = (value >> 13) & 1;
 384        s->pt = (value >> 12) & 1;
 385        s->trigger = (value >> 10) & 3;
 386        if (s->capture == gpt_capture_none &&
 387                        ((value >> 8) & 3) != gpt_capture_none)
 388            s->capt_num = 0;
 389        s->capture = (value >> 8) & 3;
 390        s->scpwm = (value >> 7) & 1;
 391        s->ce = (value >> 6) & 1;
 392        s->pre = (value >> 5) & 1;
 393        s->ptv = (value >> 2) & 7;
 394        s->ar = (value >> 1) & 1;
 395        s->st = (value >> 0) & 1;
 396        if (s->inout && s->trigger != gpt_trigger_none)
 397            fprintf(stderr, "%s: GP timer pin must be an output "
 398                            "for this trigger mode\n", __FUNCTION__);
 399        if (!s->inout && s->capture != gpt_capture_none)
 400            fprintf(stderr, "%s: GP timer pin must be an input "
 401                            "for this capture mode\n", __FUNCTION__);
 402        if (s->trigger == gpt_trigger_none)
 403            omap_gp_timer_out(s, s->scpwm);
 404        /* TODO: make sure this doesn't overflow 32-bits */
 405        s->ticks_per_sec = NANOSECONDS_PER_SECOND << (s->pre ? s->ptv + 1 : 0);
 406        omap_gp_timer_update(s);
 407        break;
 408
 409    case 0x28:  /* TCRR */
 410        s->time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
 411        s->val = value;
 412        omap_gp_timer_update(s);
 413        break;
 414
 415    case 0x2c:  /* TLDR */
 416        s->load_val = value;
 417        break;
 418
 419    case 0x30:  /* TTGR */
 420        s->time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
 421        s->val = s->load_val;
 422        omap_gp_timer_update(s);
 423        break;
 424
 425    case 0x38:  /* TMAR */
 426        omap_gp_timer_sync(s);
 427        s->match_val = value;
 428        omap_gp_timer_update(s);
 429        break;
 430
 431    case 0x40:  /* TSICR */
 432        s->posted = (value >> 2) & 1;
 433        if (value & 2)  /* How much exactly are we supposed to reset? */
 434            omap_gp_timer_reset(s);
 435        break;
 436
 437    default:
 438        OMAP_BAD_REG(addr);
 439    }
 440}
 441
 442static void omap_gp_timer_writeh(void *opaque, hwaddr addr,
 443                uint32_t value)
 444{
 445    struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque;
 446
 447    if (addr & 2)
 448        omap_gp_timer_write(opaque, addr, (value << 16) | s->writeh);
 449    else
 450        s->writeh = (uint16_t) value;
 451}
 452
 453static const MemoryRegionOps omap_gp_timer_ops = {
 454    .old_mmio = {
 455        .read = {
 456            omap_badwidth_read32,
 457            omap_gp_timer_readh,
 458            omap_gp_timer_readw,
 459        },
 460        .write = {
 461            omap_badwidth_write32,
 462            omap_gp_timer_writeh,
 463            omap_gp_timer_write,
 464        },
 465    },
 466    .endianness = DEVICE_NATIVE_ENDIAN,
 467};
 468
 469struct omap_gp_timer_s *omap_gp_timer_init(struct omap_target_agent_s *ta,
 470                qemu_irq irq, omap_clk fclk, omap_clk iclk)
 471{
 472    struct omap_gp_timer_s *s = g_new0(struct omap_gp_timer_s, 1);
 473
 474    s->ta = ta;
 475    s->irq = irq;
 476    s->clk = fclk;
 477    s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, omap_gp_timer_tick, s);
 478    s->match = timer_new_ns(QEMU_CLOCK_VIRTUAL, omap_gp_timer_match, s);
 479    s->in = qemu_allocate_irq(omap_gp_timer_input, s, 0);
 480    omap_gp_timer_reset(s);
 481    omap_gp_timer_clk_setup(s);
 482
 483    memory_region_init_io(&s->iomem, NULL, &omap_gp_timer_ops, s, "omap.gptimer",
 484                          omap_l4_region_size(ta, 0));
 485    omap_l4_attach(ta, 0, &s->iomem);
 486
 487    return s;
 488}
 489