qemu/hw/misc/npcm7xx_pwm.c
<<
>>
Prefs
   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);
 570