cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

exynos4210_pwm.c (13115B)


      1/*
      2 * Samsung exynos4210 Pulse Width Modulation Timer
      3 *
      4 * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd.
      5 * All rights reserved.
      6 *
      7 * Evgeny Voevodin <e.voevodin@samsung.com>
      8 *
      9 * This program is free software; you can redistribute it and/or modify it
     10 * under the terms of the GNU General Public License as published by the
     11 * Free Software Foundation; either version 2 of the License, or (at your
     12 * option) any later version.
     13 *
     14 * This program is distributed in the hope that it will be useful,
     15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
     17 * See the GNU General Public License for more details.
     18 *
     19 * You should have received a copy of the GNU General Public License along
     20 * with this program; if not, see <http://www.gnu.org/licenses/>.
     21 */
     22
     23#include "qemu/osdep.h"
     24#include "qemu/log.h"
     25#include "hw/sysbus.h"
     26#include "migration/vmstate.h"
     27#include "qemu/timer.h"
     28#include "qemu/module.h"
     29#include "hw/ptimer.h"
     30
     31#include "hw/arm/exynos4210.h"
     32#include "hw/irq.h"
     33#include "qom/object.h"
     34
     35//#define DEBUG_PWM
     36
     37#ifdef DEBUG_PWM
     38#define DPRINTF(fmt, ...) \
     39        do { fprintf(stdout, "PWM: [%24s:%5d] " fmt, __func__, __LINE__, \
     40                ## __VA_ARGS__); } while (0)
     41#else
     42#define DPRINTF(fmt, ...) do {} while (0)
     43#endif
     44
     45#define     EXYNOS4210_PWM_TIMERS_NUM      5
     46#define     EXYNOS4210_PWM_REG_MEM_SIZE    0x50
     47
     48#define     TCFG0        0x0000
     49#define     TCFG1        0x0004
     50#define     TCON         0x0008
     51#define     TCNTB0       0x000C
     52#define     TCMPB0       0x0010
     53#define     TCNTO0       0x0014
     54#define     TCNTB1       0x0018
     55#define     TCMPB1       0x001C
     56#define     TCNTO1       0x0020
     57#define     TCNTB2       0x0024
     58#define     TCMPB2       0x0028
     59#define     TCNTO2       0x002C
     60#define     TCNTB3       0x0030
     61#define     TCMPB3       0x0034
     62#define     TCNTO3       0x0038
     63#define     TCNTB4       0x003C
     64#define     TCNTO4       0x0040
     65#define     TINT_CSTAT   0x0044
     66
     67#define     TCNTB(x)    (0xC * (x))
     68#define     TCMPB(x)    (0xC * (x) + 1)
     69#define     TCNTO(x)    (0xC * (x) + 2)
     70
     71#define GET_PRESCALER(reg, x) (((reg) & (0xFF << (8 * (x)))) >> 8 * (x))
     72#define GET_DIVIDER(reg, x) (1 << (((reg) & (0xF << (4 * (x)))) >> (4 * (x))))
     73
     74/*
     75 * Attention! Timer4 doesn't have OUTPUT_INVERTER,
     76 * so Auto Reload bit is not accessible by macros!
     77 */
     78#define     TCON_TIMER_BASE(x)          (((x) ? 1 : 0) * 4 + 4 * (x))
     79#define     TCON_TIMER_START(x)         (1 << (TCON_TIMER_BASE(x) + 0))
     80#define     TCON_TIMER_MANUAL_UPD(x)    (1 << (TCON_TIMER_BASE(x) + 1))
     81#define     TCON_TIMER_OUTPUT_INV(x)    (1 << (TCON_TIMER_BASE(x) + 2))
     82#define     TCON_TIMER_AUTO_RELOAD(x)   (1 << (TCON_TIMER_BASE(x) + 3))
     83#define     TCON_TIMER4_AUTO_RELOAD     (1 << 22)
     84
     85#define     TINT_CSTAT_STATUS(x)        (1 << (5 + (x)))
     86#define     TINT_CSTAT_ENABLE(x)        (1 << (x))
     87
     88/* timer struct */
     89typedef struct {
     90    uint32_t    id;             /* timer id */
     91    qemu_irq    irq;            /* local timer irq */
     92    uint32_t    freq;           /* timer frequency */
     93
     94    /* use ptimer.c to represent count down timer */
     95    ptimer_state *ptimer;       /* timer  */
     96
     97    /* registers */
     98    uint32_t    reg_tcntb;      /* counter register buffer */
     99    uint32_t    reg_tcmpb;      /* compare register buffer */
    100
    101    struct Exynos4210PWMState *parent;
    102
    103} Exynos4210PWM;
    104
    105#define TYPE_EXYNOS4210_PWM "exynos4210.pwm"
    106OBJECT_DECLARE_SIMPLE_TYPE(Exynos4210PWMState, EXYNOS4210_PWM)
    107
    108struct Exynos4210PWMState {
    109    SysBusDevice parent_obj;
    110
    111    MemoryRegion iomem;
    112
    113    uint32_t    reg_tcfg[2];
    114    uint32_t    reg_tcon;
    115    uint32_t    reg_tint_cstat;
    116
    117    Exynos4210PWM timer[EXYNOS4210_PWM_TIMERS_NUM];
    118
    119};
    120
    121/*** VMState ***/
    122static const VMStateDescription vmstate_exynos4210_pwm = {
    123    .name = "exynos4210.pwm.pwm",
    124    .version_id = 1,
    125    .minimum_version_id = 1,
    126    .fields = (VMStateField[]) {
    127        VMSTATE_UINT32(id, Exynos4210PWM),
    128        VMSTATE_UINT32(freq, Exynos4210PWM),
    129        VMSTATE_PTIMER(ptimer, Exynos4210PWM),
    130        VMSTATE_UINT32(reg_tcntb, Exynos4210PWM),
    131        VMSTATE_UINT32(reg_tcmpb, Exynos4210PWM),
    132        VMSTATE_END_OF_LIST()
    133    }
    134};
    135
    136static const VMStateDescription vmstate_exynos4210_pwm_state = {
    137    .name = "exynos4210.pwm",
    138    .version_id = 1,
    139    .minimum_version_id = 1,
    140    .fields = (VMStateField[]) {
    141        VMSTATE_UINT32_ARRAY(reg_tcfg, Exynos4210PWMState, 2),
    142        VMSTATE_UINT32(reg_tcon, Exynos4210PWMState),
    143        VMSTATE_UINT32(reg_tint_cstat, Exynos4210PWMState),
    144        VMSTATE_STRUCT_ARRAY(timer, Exynos4210PWMState,
    145            EXYNOS4210_PWM_TIMERS_NUM, 0,
    146        vmstate_exynos4210_pwm, Exynos4210PWM),
    147        VMSTATE_END_OF_LIST()
    148    }
    149};
    150
    151/*
    152 * PWM update frequency.
    153 * Must be called within a ptimer_transaction_begin/commit block
    154 * for s->timer[id].ptimer.
    155 */
    156static void exynos4210_pwm_update_freq(Exynos4210PWMState *s, uint32_t id)
    157{
    158    uint32_t freq;
    159    freq = s->timer[id].freq;
    160    if (id > 1) {
    161        s->timer[id].freq = 24000000 /
    162        ((GET_PRESCALER(s->reg_tcfg[0], 1) + 1) *
    163                (GET_DIVIDER(s->reg_tcfg[1], id)));
    164    } else {
    165        s->timer[id].freq = 24000000 /
    166        ((GET_PRESCALER(s->reg_tcfg[0], 0) + 1) *
    167                (GET_DIVIDER(s->reg_tcfg[1], id)));
    168    }
    169
    170    if (freq != s->timer[id].freq) {
    171        ptimer_set_freq(s->timer[id].ptimer, s->timer[id].freq);
    172        DPRINTF("freq=%uHz\n", s->timer[id].freq);
    173    }
    174}
    175
    176/*
    177 * Counter tick handler
    178 */
    179static void exynos4210_pwm_tick(void *opaque)
    180{
    181    Exynos4210PWM *s = (Exynos4210PWM *)opaque;
    182    Exynos4210PWMState *p = (Exynos4210PWMState *)s->parent;
    183    uint32_t id = s->id;
    184    bool cmp;
    185
    186    DPRINTF("timer %u tick\n", id);
    187
    188    /* set irq status */
    189    p->reg_tint_cstat |= TINT_CSTAT_STATUS(id);
    190
    191    /* raise IRQ */
    192    if (p->reg_tint_cstat & TINT_CSTAT_ENABLE(id)) {
    193        DPRINTF("timer %u IRQ\n", id);
    194        qemu_irq_raise(p->timer[id].irq);
    195    }
    196
    197    /* reload timer */
    198    if (id != 4) {
    199        cmp = p->reg_tcon & TCON_TIMER_AUTO_RELOAD(id);
    200    } else {
    201        cmp = p->reg_tcon & TCON_TIMER4_AUTO_RELOAD;
    202    }
    203
    204    if (cmp) {
    205        DPRINTF("auto reload timer %u count to %x\n", id,
    206                p->timer[id].reg_tcntb);
    207        ptimer_set_count(p->timer[id].ptimer, p->timer[id].reg_tcntb);
    208        ptimer_run(p->timer[id].ptimer, 1);
    209    } else {
    210        /* stop timer, set status to STOP, see Basic Timer Operation */
    211        p->reg_tcon &= ~TCON_TIMER_START(id);
    212        ptimer_stop(p->timer[id].ptimer);
    213    }
    214}
    215
    216/*
    217 * PWM Read
    218 */
    219static uint64_t exynos4210_pwm_read(void *opaque, hwaddr offset,
    220        unsigned size)
    221{
    222    Exynos4210PWMState *s = (Exynos4210PWMState *)opaque;
    223    uint32_t value = 0;
    224    int index;
    225
    226    switch (offset) {
    227    case TCFG0: case TCFG1:
    228        index = (offset - TCFG0) >> 2;
    229        value = s->reg_tcfg[index];
    230        break;
    231
    232    case TCON:
    233        value = s->reg_tcon;
    234        break;
    235
    236    case TCNTB0: case TCNTB1:
    237    case TCNTB2: case TCNTB3: case TCNTB4:
    238        index = (offset - TCNTB0) / 0xC;
    239        value = s->timer[index].reg_tcntb;
    240        break;
    241
    242    case TCMPB0: case TCMPB1:
    243    case TCMPB2: case TCMPB3:
    244        index = (offset - TCMPB0) / 0xC;
    245        value = s->timer[index].reg_tcmpb;
    246        break;
    247
    248    case TCNTO0: case TCNTO1:
    249    case TCNTO2: case TCNTO3: case TCNTO4:
    250        index = (offset == TCNTO4) ? 4 : (offset - TCNTO0) / 0xC;
    251        value = ptimer_get_count(s->timer[index].ptimer);
    252        break;
    253
    254    case TINT_CSTAT:
    255        value = s->reg_tint_cstat;
    256        break;
    257
    258    default:
    259        qemu_log_mask(LOG_GUEST_ERROR,
    260                      "exynos4210.pwm: bad read offset " TARGET_FMT_plx,
    261                      offset);
    262        break;
    263    }
    264    return value;
    265}
    266
    267/*
    268 * PWM Write
    269 */
    270static void exynos4210_pwm_write(void *opaque, hwaddr offset,
    271        uint64_t value, unsigned size)
    272{
    273    Exynos4210PWMState *s = (Exynos4210PWMState *)opaque;
    274    int index;
    275    uint32_t new_val;
    276    int i;
    277
    278    switch (offset) {
    279    case TCFG0: case TCFG1:
    280        index = (offset - TCFG0) >> 2;
    281        s->reg_tcfg[index] = value;
    282
    283        /* update timers frequencies */
    284        for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) {
    285            ptimer_transaction_begin(s->timer[i].ptimer);
    286            exynos4210_pwm_update_freq(s, s->timer[i].id);
    287            ptimer_transaction_commit(s->timer[i].ptimer);
    288        }
    289        break;
    290
    291    case TCON:
    292        for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) {
    293            ptimer_transaction_begin(s->timer[i].ptimer);
    294            if ((value & TCON_TIMER_MANUAL_UPD(i)) >
    295            (s->reg_tcon & TCON_TIMER_MANUAL_UPD(i))) {
    296                /*
    297                 * TCNTB and TCMPB are loaded into TCNT and TCMP.
    298                 * Update timers.
    299                 */
    300
    301                /* this will start timer to run, this ok, because
    302                 * during processing start bit timer will be stopped
    303                 * if needed */
    304                ptimer_set_count(s->timer[i].ptimer, s->timer[i].reg_tcntb);
    305                DPRINTF("set timer %d count to %x\n", i,
    306                        s->timer[i].reg_tcntb);
    307            }
    308
    309            if ((value & TCON_TIMER_START(i)) >
    310            (s->reg_tcon & TCON_TIMER_START(i))) {
    311                /* changed to start */
    312                ptimer_run(s->timer[i].ptimer, 1);
    313                DPRINTF("run timer %d\n", i);
    314            }
    315
    316            if ((value & TCON_TIMER_START(i)) <
    317                    (s->reg_tcon & TCON_TIMER_START(i))) {
    318                /* changed to stop */
    319                ptimer_stop(s->timer[i].ptimer);
    320                DPRINTF("stop timer %d\n", i);
    321            }
    322            ptimer_transaction_commit(s->timer[i].ptimer);
    323        }
    324        s->reg_tcon = value;
    325        break;
    326
    327    case TCNTB0: case TCNTB1:
    328    case TCNTB2: case TCNTB3: case TCNTB4:
    329        index = (offset - TCNTB0) / 0xC;
    330        s->timer[index].reg_tcntb = value;
    331        break;
    332
    333    case TCMPB0: case TCMPB1:
    334    case TCMPB2: case TCMPB3:
    335        index = (offset - TCMPB0) / 0xC;
    336        s->timer[index].reg_tcmpb = value;
    337        break;
    338
    339    case TINT_CSTAT:
    340        new_val = (s->reg_tint_cstat & 0x3E0) + (0x1F & value);
    341        new_val &= ~(0x3E0 & value);
    342
    343        for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) {
    344            if ((new_val & TINT_CSTAT_STATUS(i)) <
    345                    (s->reg_tint_cstat & TINT_CSTAT_STATUS(i))) {
    346                qemu_irq_lower(s->timer[i].irq);
    347            }
    348        }
    349
    350        s->reg_tint_cstat = new_val;
    351        break;
    352
    353    default:
    354        qemu_log_mask(LOG_GUEST_ERROR,
    355                      "exynos4210.pwm: bad write offset " TARGET_FMT_plx,
    356                      offset);
    357        break;
    358
    359    }
    360}
    361
    362/*
    363 * Set default values to timer fields and registers
    364 */
    365static void exynos4210_pwm_reset(DeviceState *d)
    366{
    367    Exynos4210PWMState *s = EXYNOS4210_PWM(d);
    368    int i;
    369    s->reg_tcfg[0] = 0x0101;
    370    s->reg_tcfg[1] = 0x0;
    371    s->reg_tcon = 0;
    372    s->reg_tint_cstat = 0;
    373    for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) {
    374        s->timer[i].reg_tcmpb = 0;
    375        s->timer[i].reg_tcntb = 0;
    376
    377        ptimer_transaction_begin(s->timer[i].ptimer);
    378        exynos4210_pwm_update_freq(s, s->timer[i].id);
    379        ptimer_stop(s->timer[i].ptimer);
    380        ptimer_transaction_commit(s->timer[i].ptimer);
    381    }
    382}
    383
    384static const MemoryRegionOps exynos4210_pwm_ops = {
    385    .read = exynos4210_pwm_read,
    386    .write = exynos4210_pwm_write,
    387    .endianness = DEVICE_NATIVE_ENDIAN,
    388};
    389
    390/*
    391 * PWM timer initialization
    392 */
    393static void exynos4210_pwm_init(Object *obj)
    394{
    395    Exynos4210PWMState *s = EXYNOS4210_PWM(obj);
    396    SysBusDevice *dev = SYS_BUS_DEVICE(obj);
    397    int i;
    398
    399    for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) {
    400        sysbus_init_irq(dev, &s->timer[i].irq);
    401        s->timer[i].ptimer = ptimer_init(exynos4210_pwm_tick,
    402                                         &s->timer[i],
    403                                         PTIMER_POLICY_DEFAULT);
    404        s->timer[i].id = i;
    405        s->timer[i].parent = s;
    406    }
    407
    408    memory_region_init_io(&s->iomem, obj, &exynos4210_pwm_ops, s,
    409                          "exynos4210-pwm", EXYNOS4210_PWM_REG_MEM_SIZE);
    410    sysbus_init_mmio(dev, &s->iomem);
    411}
    412
    413static void exynos4210_pwm_finalize(Object *obj)
    414{
    415    Exynos4210PWMState *s = EXYNOS4210_PWM(obj);
    416    int i;
    417
    418    for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) {
    419        ptimer_free(s->timer[i].ptimer);
    420    }
    421}
    422
    423static void exynos4210_pwm_class_init(ObjectClass *klass, void *data)
    424{
    425    DeviceClass *dc = DEVICE_CLASS(klass);
    426
    427    dc->reset = exynos4210_pwm_reset;
    428    dc->vmsd = &vmstate_exynos4210_pwm_state;
    429}
    430
    431static const TypeInfo exynos4210_pwm_info = {
    432    .name          = TYPE_EXYNOS4210_PWM,
    433    .parent        = TYPE_SYS_BUS_DEVICE,
    434    .instance_size = sizeof(Exynos4210PWMState),
    435    .instance_init = exynos4210_pwm_init,
    436    .instance_finalize = exynos4210_pwm_finalize,
    437    .class_init    = exynos4210_pwm_class_init,
    438};
    439
    440static void exynos4210_pwm_register_types(void)
    441{
    442    type_register_static(&exynos4210_pwm_info);
    443}
    444
    445type_init(exynos4210_pwm_register_types)