qemu

Форк
0
/
npcm7xx_pwm.c 
569 строк · 15.5 Кб
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

31
REG32(NPCM7XX_PWM_PPR, 0x00);
32
REG32(NPCM7XX_PWM_CSR, 0x04);
33
REG32(NPCM7XX_PWM_PCR, 0x08);
34
REG32(NPCM7XX_PWM_CNR0, 0x0c);
35
REG32(NPCM7XX_PWM_CMR0, 0x10);
36
REG32(NPCM7XX_PWM_PDR0, 0x14);
37
REG32(NPCM7XX_PWM_CNR1, 0x18);
38
REG32(NPCM7XX_PWM_CMR1, 0x1c);
39
REG32(NPCM7XX_PWM_PDR1, 0x20);
40
REG32(NPCM7XX_PWM_CNR2, 0x24);
41
REG32(NPCM7XX_PWM_CMR2, 0x28);
42
REG32(NPCM7XX_PWM_PDR2, 0x2c);
43
REG32(NPCM7XX_PWM_CNR3, 0x30);
44
REG32(NPCM7XX_PWM_CMR3, 0x34);
45
REG32(NPCM7XX_PWM_PDR3, 0x38);
46
REG32(NPCM7XX_PWM_PIER, 0x3c);
47
REG32(NPCM7XX_PWM_PIIR, 0x40);
48
REG32(NPCM7XX_PWM_PWDR0, 0x44);
49
REG32(NPCM7XX_PWM_PWDR1, 0x48);
50
REG32(NPCM7XX_PWM_PWDR2, 0x4c);
51
REG32(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. */
65
static const int npcm7xx_ppr_base[] = { 0, 0, 8, 8 };
66
/* Offset of each PWM channel's clock selector in the CSR register. */
67
static const int npcm7xx_csr_base[] = { 0, 4, 8, 12 };
68
/* Offset of each PWM channel's control variable in the PCR register. */
69
static const int npcm7xx_ch_base[] = { 0, 8, 12, 16 };
70

71
static 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

100
static 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

123
static 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

134
static 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

146
static void npcm7xx_pwm_update_output(NPCM7xxPWM *p)
147
{
148
    npcm7xx_pwm_update_freq(p);
149
    npcm7xx_pwm_update_duty(p);
150
}
151

152
static 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

166
static 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

180
static 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

221
static 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

237
static 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

253
static 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

269
static 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

285
static 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

350
static 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

439
static 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

450
static 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

471
static void npcm7xx_pwm_hold_reset(Object *obj, ResetType type)
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

481
static 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

510
static const VMStateDescription vmstate_npcm7xx_pwm = {
511
    .name = "npcm7xx-pwm",
512
    .version_id = 0,
513
    .minimum_version_id = 0,
514
    .fields = (const 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

528
static const VMStateDescription vmstate_npcm7xx_pwm_module = {
529
    .name = "npcm7xx-pwm-module",
530
    .version_id = 0,
531
    .minimum_version_id = 0,
532
    .fields = (const 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

546
static 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

557
static 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

565
static void npcm7xx_pwm_register_type(void)
566
{
567
    type_register_static(&npcm7xx_pwm_info);
568
}
569
type_init(npcm7xx_pwm_register_type);
570

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.