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

npcm7xx_pwm.c (15870B)


      1/*
      2 * Nuvoton NPCM7xx PWM Module
      3 *
      4 * Copyright 2020 Google LLC
      5 *
      6 * This program is free software; you can redistribute it and/or modify it
      7 * under the terms of the GNU General Public License as published by the
      8 * Free Software Foundation; either version 2 of the License, or
      9 * (at your option) any later version.
     10 *
     11 * This program is distributed in the hope that it will be useful, but WITHOUT
     12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
     13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
     14 * for more details.
     15 */
     16
     17#include "qemu/osdep.h"
     18#include "hw/irq.h"
     19#include "hw/qdev-clock.h"
     20#include "hw/qdev-properties.h"
     21#include "hw/misc/npcm7xx_pwm.h"
     22#include "hw/registerfields.h"
     23#include "migration/vmstate.h"
     24#include "qemu/bitops.h"
     25#include "qemu/error-report.h"
     26#include "qemu/log.h"
     27#include "qemu/module.h"
     28#include "qemu/units.h"
     29#include "trace.h"
     30
     31REG32(NPCM7XX_PWM_PPR, 0x00);
     32REG32(NPCM7XX_PWM_CSR, 0x04);
     33REG32(NPCM7XX_PWM_PCR, 0x08);
     34REG32(NPCM7XX_PWM_CNR0, 0x0c);
     35REG32(NPCM7XX_PWM_CMR0, 0x10);
     36REG32(NPCM7XX_PWM_PDR0, 0x14);
     37REG32(NPCM7XX_PWM_CNR1, 0x18);
     38REG32(NPCM7XX_PWM_CMR1, 0x1c);
     39REG32(NPCM7XX_PWM_PDR1, 0x20);
     40REG32(NPCM7XX_PWM_CNR2, 0x24);
     41REG32(NPCM7XX_PWM_CMR2, 0x28);
     42REG32(NPCM7XX_PWM_PDR2, 0x2c);
     43REG32(NPCM7XX_PWM_CNR3, 0x30);
     44REG32(NPCM7XX_PWM_CMR3, 0x34);
     45REG32(NPCM7XX_PWM_PDR3, 0x38);
     46REG32(NPCM7XX_PWM_PIER, 0x3c);
     47REG32(NPCM7XX_PWM_PIIR, 0x40);
     48REG32(NPCM7XX_PWM_PWDR0, 0x44);
     49REG32(NPCM7XX_PWM_PWDR1, 0x48);
     50REG32(NPCM7XX_PWM_PWDR2, 0x4c);
     51REG32(NPCM7XX_PWM_PWDR3, 0x50);
     52
     53/* Register field definitions. */
     54#define NPCM7XX_PPR(rv, index)      extract32((rv), npcm7xx_ppr_base[index], 8)
     55#define NPCM7XX_CSR(rv, index)      extract32((rv), npcm7xx_csr_base[index], 3)
     56#define NPCM7XX_CH(rv, index)       extract32((rv), npcm7xx_ch_base[index], 4)
     57#define NPCM7XX_CH_EN               BIT(0)
     58#define NPCM7XX_CH_INV              BIT(2)
     59#define NPCM7XX_CH_MOD              BIT(3)
     60
     61#define NPCM7XX_MAX_CMR             65535
     62#define NPCM7XX_MAX_CNR             65535
     63
     64/* Offset of each PWM channel's prescaler in the PPR register. */
     65static const int npcm7xx_ppr_base[] = { 0, 0, 8, 8 };
     66/* Offset of each PWM channel's clock selector in the CSR register. */
     67static const int npcm7xx_csr_base[] = { 0, 4, 8, 12 };
     68/* Offset of each PWM channel's control variable in the PCR register. */
     69static const int npcm7xx_ch_base[] = { 0, 8, 12, 16 };
     70
     71static uint32_t npcm7xx_pwm_calculate_freq(NPCM7xxPWM *p)
     72{
     73    uint32_t ppr;
     74    uint32_t csr;
     75    uint32_t freq;
     76
     77    if (!p->running) {
     78        return 0;
     79    }
     80
     81    csr = NPCM7XX_CSR(p->module->csr, p->index);
     82    ppr = NPCM7XX_PPR(p->module->ppr, p->index);
     83    freq = clock_get_hz(p->module->clock);
     84    freq /= ppr + 1;
     85    /* csr can only be 0~4 */
     86    if (csr > 4) {
     87        qemu_log_mask(LOG_GUEST_ERROR,
     88                      "%s: invalid csr value %u\n",
     89                      __func__, csr);
     90        csr = 4;
     91    }
     92    /* freq won't be changed if csr == 4. */
     93    if (csr < 4) {
     94        freq >>= csr + 1;
     95    }
     96
     97    return freq / (p->cnr + 1);
     98}
     99
    100static uint32_t npcm7xx_pwm_calculate_duty(NPCM7xxPWM *p)
    101{
    102    uint32_t duty;
    103
    104    if (p->running) {
    105        if (p->cnr == 0) {
    106            duty = 0;
    107        } else if (p->cmr >= p->cnr) {
    108            duty = NPCM7XX_PWM_MAX_DUTY;
    109        } else {
    110            duty = (uint64_t)NPCM7XX_PWM_MAX_DUTY * (p->cmr + 1) / (p->cnr + 1);
    111        }
    112    } else {
    113        duty = 0;
    114    }
    115
    116    if (p->inverted) {
    117        duty = NPCM7XX_PWM_MAX_DUTY - duty;
    118    }
    119
    120    return duty;
    121}
    122
    123static void npcm7xx_pwm_update_freq(NPCM7xxPWM *p)
    124{
    125    uint32_t freq = npcm7xx_pwm_calculate_freq(p);
    126
    127    if (freq != p->freq) {
    128        trace_npcm7xx_pwm_update_freq(DEVICE(p->module)->canonical_path,
    129                                      p->index, p->freq, freq);
    130        p->freq = freq;
    131    }
    132}
    133
    134static void npcm7xx_pwm_update_duty(NPCM7xxPWM *p)
    135{
    136    uint32_t duty = npcm7xx_pwm_calculate_duty(p);
    137
    138    if (duty != p->duty) {
    139        trace_npcm7xx_pwm_update_duty(DEVICE(p->module)->canonical_path,
    140                                      p->index, p->duty, duty);
    141        p->duty = duty;
    142        qemu_set_irq(p->module->duty_gpio_out[p->index], p->duty);
    143    }
    144}
    145
    146static void npcm7xx_pwm_update_output(NPCM7xxPWM *p)
    147{
    148    npcm7xx_pwm_update_freq(p);
    149    npcm7xx_pwm_update_duty(p);
    150}
    151
    152static void npcm7xx_pwm_write_ppr(NPCM7xxPWMState *s, uint32_t new_ppr)
    153{
    154    int i;
    155    uint32_t old_ppr = s->ppr;
    156
    157    QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_ppr_base) != NPCM7XX_PWM_PER_MODULE);
    158    s->ppr = new_ppr;
    159    for (i = 0; i < NPCM7XX_PWM_PER_MODULE; ++i) {
    160        if (NPCM7XX_PPR(old_ppr, i) != NPCM7XX_PPR(new_ppr, i)) {
    161            npcm7xx_pwm_update_freq(&s->pwm[i]);
    162        }
    163    }
    164}
    165
    166static void npcm7xx_pwm_write_csr(NPCM7xxPWMState *s, uint32_t new_csr)
    167{
    168    int i;
    169    uint32_t old_csr = s->csr;
    170
    171    QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_csr_base) != NPCM7XX_PWM_PER_MODULE);
    172    s->csr = new_csr;
    173    for (i = 0; i < NPCM7XX_PWM_PER_MODULE; ++i) {
    174        if (NPCM7XX_CSR(old_csr, i) != NPCM7XX_CSR(new_csr, i)) {
    175            npcm7xx_pwm_update_freq(&s->pwm[i]);
    176        }
    177    }
    178}
    179
    180static void npcm7xx_pwm_write_pcr(NPCM7xxPWMState *s, uint32_t new_pcr)
    181{
    182    int i;
    183    bool inverted;
    184    uint32_t pcr;
    185    NPCM7xxPWM *p;
    186
    187    s->pcr = new_pcr;
    188    QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_ch_base) != NPCM7XX_PWM_PER_MODULE);
    189    for (i = 0; i < NPCM7XX_PWM_PER_MODULE; ++i) {
    190        p = &s->pwm[i];
    191        pcr = NPCM7XX_CH(new_pcr, i);
    192        inverted = pcr & NPCM7XX_CH_INV;
    193
    194        /*
    195         * We only run a PWM channel with toggle mode. Single-shot mode does not
    196         * generate frequency and duty-cycle values.
    197         */
    198        if ((pcr & NPCM7XX_CH_EN) && (pcr & NPCM7XX_CH_MOD)) {
    199            if (p->running) {
    200                /* Re-run this PWM channel if inverted changed. */
    201                if (p->inverted ^ inverted) {
    202                    p->inverted = inverted;
    203                    npcm7xx_pwm_update_duty(p);
    204                }
    205            } else {
    206                /* Run this PWM channel. */
    207                p->running = true;
    208                p->inverted = inverted;
    209                npcm7xx_pwm_update_output(p);
    210            }
    211        } else {
    212            /* Clear this PWM channel. */
    213            p->running = false;
    214            p->inverted = inverted;
    215            npcm7xx_pwm_update_output(p);
    216        }
    217    }
    218
    219}
    220
    221static hwaddr npcm7xx_cnr_index(hwaddr offset)
    222{
    223    switch (offset) {
    224    case A_NPCM7XX_PWM_CNR0:
    225        return 0;
    226    case A_NPCM7XX_PWM_CNR1:
    227        return 1;
    228    case A_NPCM7XX_PWM_CNR2:
    229        return 2;
    230    case A_NPCM7XX_PWM_CNR3:
    231        return 3;
    232    default:
    233        g_assert_not_reached();
    234    }
    235}
    236
    237static hwaddr npcm7xx_cmr_index(hwaddr offset)
    238{
    239    switch (offset) {
    240    case A_NPCM7XX_PWM_CMR0:
    241        return 0;
    242    case A_NPCM7XX_PWM_CMR1:
    243        return 1;
    244    case A_NPCM7XX_PWM_CMR2:
    245        return 2;
    246    case A_NPCM7XX_PWM_CMR3:
    247        return 3;
    248    default:
    249        g_assert_not_reached();
    250    }
    251}
    252
    253static hwaddr npcm7xx_pdr_index(hwaddr offset)
    254{
    255    switch (offset) {
    256    case A_NPCM7XX_PWM_PDR0:
    257        return 0;
    258    case A_NPCM7XX_PWM_PDR1:
    259        return 1;
    260    case A_NPCM7XX_PWM_PDR2:
    261        return 2;
    262    case A_NPCM7XX_PWM_PDR3:
    263        return 3;
    264    default:
    265        g_assert_not_reached();
    266    }
    267}
    268
    269static hwaddr npcm7xx_pwdr_index(hwaddr offset)
    270{
    271    switch (offset) {
    272    case A_NPCM7XX_PWM_PWDR0:
    273        return 0;
    274    case A_NPCM7XX_PWM_PWDR1:
    275        return 1;
    276    case A_NPCM7XX_PWM_PWDR2:
    277        return 2;
    278    case A_NPCM7XX_PWM_PWDR3:
    279        return 3;
    280    default:
    281        g_assert_not_reached();
    282    }
    283}
    284
    285static uint64_t npcm7xx_pwm_read(void *opaque, hwaddr offset, unsigned size)
    286{
    287    NPCM7xxPWMState *s = opaque;
    288    uint64_t value = 0;
    289
    290    switch (offset) {
    291    case A_NPCM7XX_PWM_CNR0:
    292    case A_NPCM7XX_PWM_CNR1:
    293    case A_NPCM7XX_PWM_CNR2:
    294    case A_NPCM7XX_PWM_CNR3:
    295        value = s->pwm[npcm7xx_cnr_index(offset)].cnr;
    296        break;
    297
    298    case A_NPCM7XX_PWM_CMR0:
    299    case A_NPCM7XX_PWM_CMR1:
    300    case A_NPCM7XX_PWM_CMR2:
    301    case A_NPCM7XX_PWM_CMR3:
    302        value = s->pwm[npcm7xx_cmr_index(offset)].cmr;
    303        break;
    304
    305    case A_NPCM7XX_PWM_PDR0:
    306    case A_NPCM7XX_PWM_PDR1:
    307    case A_NPCM7XX_PWM_PDR2:
    308    case A_NPCM7XX_PWM_PDR3:
    309        value = s->pwm[npcm7xx_pdr_index(offset)].pdr;
    310        break;
    311
    312    case A_NPCM7XX_PWM_PWDR0:
    313    case A_NPCM7XX_PWM_PWDR1:
    314    case A_NPCM7XX_PWM_PWDR2:
    315    case A_NPCM7XX_PWM_PWDR3:
    316        value = s->pwm[npcm7xx_pwdr_index(offset)].pwdr;
    317        break;
    318
    319    case A_NPCM7XX_PWM_PPR:
    320        value = s->ppr;
    321        break;
    322
    323    case A_NPCM7XX_PWM_CSR:
    324        value = s->csr;
    325        break;
    326
    327    case A_NPCM7XX_PWM_PCR:
    328        value = s->pcr;
    329        break;
    330
    331    case A_NPCM7XX_PWM_PIER:
    332        value = s->pier;
    333        break;
    334
    335    case A_NPCM7XX_PWM_PIIR:
    336        value = s->piir;
    337        break;
    338
    339    default:
    340        qemu_log_mask(LOG_GUEST_ERROR,
    341                      "%s: invalid offset 0x%04" HWADDR_PRIx "\n",
    342                      __func__, offset);
    343        break;
    344    }
    345
    346    trace_npcm7xx_pwm_read(DEVICE(s)->canonical_path, offset, value);
    347    return value;
    348}
    349
    350static void npcm7xx_pwm_write(void *opaque, hwaddr offset,
    351                                uint64_t v, unsigned size)
    352{
    353    NPCM7xxPWMState *s = opaque;
    354    NPCM7xxPWM *p;
    355    uint32_t value = v;
    356
    357    trace_npcm7xx_pwm_write(DEVICE(s)->canonical_path, offset, value);
    358    switch (offset) {
    359    case A_NPCM7XX_PWM_CNR0:
    360    case A_NPCM7XX_PWM_CNR1:
    361    case A_NPCM7XX_PWM_CNR2:
    362    case A_NPCM7XX_PWM_CNR3:
    363        p = &s->pwm[npcm7xx_cnr_index(offset)];
    364        if (value > NPCM7XX_MAX_CNR) {
    365            qemu_log_mask(LOG_GUEST_ERROR,
    366                          "%s: invalid cnr value: %u", __func__, value);
    367            p->cnr = NPCM7XX_MAX_CNR;
    368        } else {
    369            p->cnr = value;
    370        }
    371        npcm7xx_pwm_update_output(p);
    372        break;
    373
    374    case A_NPCM7XX_PWM_CMR0:
    375    case A_NPCM7XX_PWM_CMR1:
    376    case A_NPCM7XX_PWM_CMR2:
    377    case A_NPCM7XX_PWM_CMR3:
    378        p = &s->pwm[npcm7xx_cmr_index(offset)];
    379        if (value > NPCM7XX_MAX_CMR) {
    380            qemu_log_mask(LOG_GUEST_ERROR,
    381                          "%s: invalid cmr value: %u", __func__, value);
    382            p->cmr = NPCM7XX_MAX_CMR;
    383        } else {
    384            p->cmr = value;
    385        }
    386        npcm7xx_pwm_update_output(p);
    387        break;
    388
    389    case A_NPCM7XX_PWM_PDR0:
    390    case A_NPCM7XX_PWM_PDR1:
    391    case A_NPCM7XX_PWM_PDR2:
    392    case A_NPCM7XX_PWM_PDR3:
    393        qemu_log_mask(LOG_GUEST_ERROR,
    394                      "%s: register @ 0x%04" HWADDR_PRIx " is read-only\n",
    395                      __func__, offset);
    396        break;
    397
    398    case A_NPCM7XX_PWM_PWDR0:
    399    case A_NPCM7XX_PWM_PWDR1:
    400    case A_NPCM7XX_PWM_PWDR2:
    401    case A_NPCM7XX_PWM_PWDR3:
    402        qemu_log_mask(LOG_UNIMP,
    403                     "%s: register @ 0x%04" HWADDR_PRIx " is not implemented\n",
    404                     __func__, offset);
    405        break;
    406
    407    case A_NPCM7XX_PWM_PPR:
    408        npcm7xx_pwm_write_ppr(s, value);
    409        break;
    410
    411    case A_NPCM7XX_PWM_CSR:
    412        npcm7xx_pwm_write_csr(s, value);
    413        break;
    414
    415    case A_NPCM7XX_PWM_PCR:
    416        npcm7xx_pwm_write_pcr(s, value);
    417        break;
    418
    419    case A_NPCM7XX_PWM_PIER:
    420        qemu_log_mask(LOG_UNIMP,
    421                     "%s: register @ 0x%04" HWADDR_PRIx " is not implemented\n",
    422                     __func__, offset);
    423        break;
    424
    425    case A_NPCM7XX_PWM_PIIR:
    426        qemu_log_mask(LOG_UNIMP,
    427                     "%s: register @ 0x%04" HWADDR_PRIx " is not implemented\n",
    428                     __func__, offset);
    429        break;
    430
    431    default:
    432        qemu_log_mask(LOG_GUEST_ERROR,
    433                      "%s: invalid offset 0x%04" HWADDR_PRIx "\n",
    434                      __func__, offset);
    435        break;
    436    }
    437}
    438
    439static const struct MemoryRegionOps npcm7xx_pwm_ops = {
    440    .read       = npcm7xx_pwm_read,
    441    .write      = npcm7xx_pwm_write,
    442    .endianness = DEVICE_LITTLE_ENDIAN,
    443    .valid      = {
    444        .min_access_size        = 4,
    445        .max_access_size        = 4,
    446        .unaligned              = false,
    447    },
    448};
    449
    450static void npcm7xx_pwm_enter_reset(Object *obj, ResetType type)
    451{
    452    NPCM7xxPWMState *s = NPCM7XX_PWM(obj);
    453    int i;
    454
    455    for (i = 0; i < NPCM7XX_PWM_PER_MODULE; i++) {
    456        NPCM7xxPWM *p = &s->pwm[i];
    457
    458        p->cnr = 0x00000000;
    459        p->cmr = 0x00000000;
    460        p->pdr = 0x00000000;
    461        p->pwdr = 0x00000000;
    462    }
    463
    464    s->ppr = 0x00000000;
    465    s->csr = 0x00000000;
    466    s->pcr = 0x00000000;
    467    s->pier = 0x00000000;
    468    s->piir = 0x00000000;
    469}
    470
    471static void npcm7xx_pwm_hold_reset(Object *obj)
    472{
    473    NPCM7xxPWMState *s = NPCM7XX_PWM(obj);
    474    int i;
    475
    476    for (i = 0; i < NPCM7XX_PWM_PER_MODULE; i++) {
    477        qemu_irq_lower(s->pwm[i].irq);
    478    }
    479}
    480
    481static void npcm7xx_pwm_init(Object *obj)
    482{
    483    NPCM7xxPWMState *s = NPCM7XX_PWM(obj);
    484    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
    485    int i;
    486
    487    QEMU_BUILD_BUG_ON(ARRAY_SIZE(s->pwm) != NPCM7XX_PWM_PER_MODULE);
    488    for (i = 0; i < NPCM7XX_PWM_PER_MODULE; i++) {
    489        NPCM7xxPWM *p = &s->pwm[i];
    490        p->module = s;
    491        p->index = i;
    492        sysbus_init_irq(sbd, &p->irq);
    493    }
    494
    495    memory_region_init_io(&s->iomem, obj, &npcm7xx_pwm_ops, s,
    496                          TYPE_NPCM7XX_PWM, 4 * KiB);
    497    sysbus_init_mmio(sbd, &s->iomem);
    498    s->clock = qdev_init_clock_in(DEVICE(s), "clock", NULL, NULL, 0);
    499
    500    for (i = 0; i < NPCM7XX_PWM_PER_MODULE; ++i) {
    501        object_property_add_uint32_ptr(obj, "freq[*]",
    502                &s->pwm[i].freq, OBJ_PROP_FLAG_READ);
    503        object_property_add_uint32_ptr(obj, "duty[*]",
    504                &s->pwm[i].duty, OBJ_PROP_FLAG_READ);
    505    }
    506    qdev_init_gpio_out_named(DEVICE(s), s->duty_gpio_out,
    507                             "duty-gpio-out", NPCM7XX_PWM_PER_MODULE);
    508}
    509
    510static const VMStateDescription vmstate_npcm7xx_pwm = {
    511    .name = "npcm7xx-pwm",
    512    .version_id = 0,
    513    .minimum_version_id = 0,
    514    .fields = (VMStateField[]) {
    515        VMSTATE_BOOL(running, NPCM7xxPWM),
    516        VMSTATE_BOOL(inverted, NPCM7xxPWM),
    517        VMSTATE_UINT8(index, NPCM7xxPWM),
    518        VMSTATE_UINT32(cnr, NPCM7xxPWM),
    519        VMSTATE_UINT32(cmr, NPCM7xxPWM),
    520        VMSTATE_UINT32(pdr, NPCM7xxPWM),
    521        VMSTATE_UINT32(pwdr, NPCM7xxPWM),
    522        VMSTATE_UINT32(freq, NPCM7xxPWM),
    523        VMSTATE_UINT32(duty, NPCM7xxPWM),
    524        VMSTATE_END_OF_LIST(),
    525    },
    526};
    527
    528static const VMStateDescription vmstate_npcm7xx_pwm_module = {
    529    .name = "npcm7xx-pwm-module",
    530    .version_id = 0,
    531    .minimum_version_id = 0,
    532    .fields = (VMStateField[]) {
    533        VMSTATE_CLOCK(clock, NPCM7xxPWMState),
    534        VMSTATE_STRUCT_ARRAY(pwm, NPCM7xxPWMState,
    535                             NPCM7XX_PWM_PER_MODULE, 0, vmstate_npcm7xx_pwm,
    536                             NPCM7xxPWM),
    537        VMSTATE_UINT32(ppr, NPCM7xxPWMState),
    538        VMSTATE_UINT32(csr, NPCM7xxPWMState),
    539        VMSTATE_UINT32(pcr, NPCM7xxPWMState),
    540        VMSTATE_UINT32(pier, NPCM7xxPWMState),
    541        VMSTATE_UINT32(piir, NPCM7xxPWMState),
    542        VMSTATE_END_OF_LIST(),
    543    },
    544};
    545
    546static void npcm7xx_pwm_class_init(ObjectClass *klass, void *data)
    547{
    548    ResettableClass *rc = RESETTABLE_CLASS(klass);
    549    DeviceClass *dc = DEVICE_CLASS(klass);
    550
    551    dc->desc = "NPCM7xx PWM Controller";
    552    dc->vmsd = &vmstate_npcm7xx_pwm_module;
    553    rc->phases.enter = npcm7xx_pwm_enter_reset;
    554    rc->phases.hold = npcm7xx_pwm_hold_reset;
    555}
    556
    557static const TypeInfo npcm7xx_pwm_info = {
    558    .name               = TYPE_NPCM7XX_PWM,
    559    .parent             = TYPE_SYS_BUS_DEVICE,
    560    .instance_size      = sizeof(NPCM7xxPWMState),
    561    .class_init         = npcm7xx_pwm_class_init,
    562    .instance_init      = npcm7xx_pwm_init,
    563};
    564
    565static void npcm7xx_pwm_register_type(void)
    566{
    567    type_register_static(&npcm7xx_pwm_info);
    568}
    569type_init(npcm7xx_pwm_register_type);