Annotation of qemu/hw/exynos4210_pwm.c, revision 1.1.1.1

1.1       root        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 <[email protected]>
                      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 "sysbus.h"
                     24: #include "qemu-timer.h"
                     25: #include "qemu-common.h"
                     26: #include "ptimer.h"
                     27: 
                     28: #include "exynos4210.h"
                     29: 
                     30: //#define DEBUG_PWM
                     31: 
                     32: #ifdef DEBUG_PWM
                     33: #define DPRINTF(fmt, ...) \
                     34:         do { fprintf(stdout, "PWM: [%24s:%5d] " fmt, __func__, __LINE__, \
                     35:                 ## __VA_ARGS__); } while (0)
                     36: #else
                     37: #define DPRINTF(fmt, ...) do {} while (0)
                     38: #endif
                     39: 
                     40: #define     EXYNOS4210_PWM_TIMERS_NUM      5
                     41: #define     EXYNOS4210_PWM_REG_MEM_SIZE    0x50
                     42: 
                     43: #define     TCFG0        0x0000
                     44: #define     TCFG1        0x0004
                     45: #define     TCON         0x0008
                     46: #define     TCNTB0       0x000C
                     47: #define     TCMPB0       0x0010
                     48: #define     TCNTO0       0x0014
                     49: #define     TCNTB1       0x0018
                     50: #define     TCMPB1       0x001C
                     51: #define     TCNTO1       0x0020
                     52: #define     TCNTB2       0x0024
                     53: #define     TCMPB2       0x0028
                     54: #define     TCNTO2       0x002C
                     55: #define     TCNTB3       0x0030
                     56: #define     TCMPB3       0x0034
                     57: #define     TCNTO3       0x0038
                     58: #define     TCNTB4       0x003C
                     59: #define     TCNTO4       0x0040
                     60: #define     TINT_CSTAT   0x0044
                     61: 
                     62: #define     TCNTB(x)    (0xC * (x))
                     63: #define     TCMPB(x)    (0xC * (x) + 1)
                     64: #define     TCNTO(x)    (0xC * (x) + 2)
                     65: 
                     66: #define GET_PRESCALER(reg, x) (((reg) & (0xFF << (8 * (x)))) >> 8 * (x))
                     67: #define GET_DIVIDER(reg, x) (1 << (((reg) & (0xF << (4 * (x)))) >> (4 * (x))))
                     68: 
                     69: /*
                     70:  * Attention! Timer4 doesn't have OUTPUT_INVERTER,
                     71:  * so Auto Reload bit is not accessible by macros!
                     72:  */
                     73: #define     TCON_TIMER_BASE(x)          (((x) ? 1 : 0) * 4 + 4 * (x))
                     74: #define     TCON_TIMER_START(x)         (1 << (TCON_TIMER_BASE(x) + 0))
                     75: #define     TCON_TIMER_MANUAL_UPD(x)    (1 << (TCON_TIMER_BASE(x) + 1))
                     76: #define     TCON_TIMER_OUTPUT_INV(x)    (1 << (TCON_TIMER_BASE(x) + 2))
                     77: #define     TCON_TIMER_AUTO_RELOAD(x)   (1 << (TCON_TIMER_BASE(x) + 3))
                     78: #define     TCON_TIMER4_AUTO_RELOAD     (1 << 22)
                     79: 
                     80: #define     TINT_CSTAT_STATUS(x)        (1 << (5 + (x)))
                     81: #define     TINT_CSTAT_ENABLE(x)        (1 << (x))
                     82: 
                     83: /* timer struct */
                     84: typedef struct {
                     85:     uint32_t    id;             /* timer id */
                     86:     qemu_irq    irq;            /* local timer irq */
                     87:     uint32_t    freq;           /* timer frequency */
                     88: 
                     89:     /* use ptimer.c to represent count down timer */
                     90:     ptimer_state *ptimer;       /* timer  */
                     91: 
                     92:     /* registers */
                     93:     uint32_t    reg_tcntb;      /* counter register buffer */
                     94:     uint32_t    reg_tcmpb;      /* compare register buffer */
                     95: 
                     96:     struct Exynos4210PWMState *parent;
                     97: 
                     98: } Exynos4210PWM;
                     99: 
                    100: 
                    101: typedef struct Exynos4210PWMState {
                    102:     SysBusDevice busdev;
                    103:     MemoryRegion iomem;
                    104: 
                    105:     uint32_t    reg_tcfg[2];
                    106:     uint32_t    reg_tcon;
                    107:     uint32_t    reg_tint_cstat;
                    108: 
                    109:     Exynos4210PWM timer[EXYNOS4210_PWM_TIMERS_NUM];
                    110: 
                    111: } Exynos4210PWMState;
                    112: 
                    113: /*** VMState ***/
                    114: static const VMStateDescription vmstate_exynos4210_pwm = {
                    115:     .name = "exynos4210.pwm.pwm",
                    116:     .version_id = 1,
                    117:     .minimum_version_id = 1,
                    118:     .minimum_version_id_old = 1,
                    119:     .fields = (VMStateField[]) {
                    120:         VMSTATE_UINT32(id, Exynos4210PWM),
                    121:         VMSTATE_UINT32(freq, Exynos4210PWM),
                    122:         VMSTATE_PTIMER(ptimer, Exynos4210PWM),
                    123:         VMSTATE_UINT32(reg_tcntb, Exynos4210PWM),
                    124:         VMSTATE_UINT32(reg_tcmpb, Exynos4210PWM),
                    125:         VMSTATE_END_OF_LIST()
                    126:     }
                    127: };
                    128: 
                    129: static const VMStateDescription vmstate_exynos4210_pwm_state = {
                    130:     .name = "exynos4210.pwm",
                    131:     .version_id = 1,
                    132:     .minimum_version_id = 1,
                    133:     .minimum_version_id_old = 1,
                    134:     .fields = (VMStateField[]) {
                    135:         VMSTATE_UINT32_ARRAY(reg_tcfg, Exynos4210PWMState, 2),
                    136:         VMSTATE_UINT32(reg_tcon, Exynos4210PWMState),
                    137:         VMSTATE_UINT32(reg_tint_cstat, Exynos4210PWMState),
                    138:         VMSTATE_STRUCT_ARRAY(timer, Exynos4210PWMState,
                    139:             EXYNOS4210_PWM_TIMERS_NUM, 0,
                    140:         vmstate_exynos4210_pwm, Exynos4210PWM),
                    141:         VMSTATE_END_OF_LIST()
                    142:     }
                    143: };
                    144: 
                    145: /*
                    146:  * PWM update frequency
                    147:  */
                    148: static void exynos4210_pwm_update_freq(Exynos4210PWMState *s, uint32_t id)
                    149: {
                    150:     uint32_t freq;
                    151:     freq = s->timer[id].freq;
                    152:     if (id > 1) {
                    153:         s->timer[id].freq = 24000000 /
                    154:         ((GET_PRESCALER(s->reg_tcfg[0], 1) + 1) *
                    155:                 (GET_DIVIDER(s->reg_tcfg[1], id)));
                    156:     } else {
                    157:         s->timer[id].freq = 24000000 /
                    158:         ((GET_PRESCALER(s->reg_tcfg[0], 0) + 1) *
                    159:                 (GET_DIVIDER(s->reg_tcfg[1], id)));
                    160:     }
                    161: 
                    162:     if (freq != s->timer[id].freq) {
                    163:         ptimer_set_freq(s->timer[id].ptimer, s->timer[id].freq);
                    164:         DPRINTF("freq=%dHz\n", s->timer[id].freq);
                    165:     }
                    166: }
                    167: 
                    168: /*
                    169:  * Counter tick handler
                    170:  */
                    171: static void exynos4210_pwm_tick(void *opaque)
                    172: {
                    173:     Exynos4210PWM *s = (Exynos4210PWM *)opaque;
                    174:     Exynos4210PWMState *p = (Exynos4210PWMState *)s->parent;
                    175:     uint32_t id = s->id;
                    176:     bool cmp;
                    177: 
                    178:     DPRINTF("timer %d tick\n", id);
                    179: 
                    180:     /* set irq status */
                    181:     p->reg_tint_cstat |= TINT_CSTAT_STATUS(id);
                    182: 
                    183:     /* raise IRQ */
                    184:     if (p->reg_tint_cstat & TINT_CSTAT_ENABLE(id)) {
                    185:         DPRINTF("timer %d IRQ\n", id);
                    186:         qemu_irq_raise(p->timer[id].irq);
                    187:     }
                    188: 
                    189:     /* reload timer */
                    190:     if (id != 4) {
                    191:         cmp = p->reg_tcon & TCON_TIMER_AUTO_RELOAD(id);
                    192:     } else {
                    193:         cmp = p->reg_tcon & TCON_TIMER4_AUTO_RELOAD;
                    194:     }
                    195: 
                    196:     if (cmp) {
                    197:         DPRINTF("auto reload timer %d count to %x\n", id,
                    198:                 p->timer[id].reg_tcntb);
                    199:         ptimer_set_count(p->timer[id].ptimer, p->timer[id].reg_tcntb);
                    200:         ptimer_run(p->timer[id].ptimer, 1);
                    201:     } else {
                    202:         /* stop timer, set status to STOP, see Basic Timer Operation */
                    203:         p->reg_tcon = ~TCON_TIMER_START(id);
                    204:         ptimer_stop(p->timer[id].ptimer);
                    205:     }
                    206: }
                    207: 
                    208: /*
                    209:  * PWM Read
                    210:  */
                    211: static uint64_t exynos4210_pwm_read(void *opaque, target_phys_addr_t offset,
                    212:         unsigned size)
                    213: {
                    214:     Exynos4210PWMState *s = (Exynos4210PWMState *)opaque;
                    215:     uint32_t value = 0;
                    216:     int index;
                    217: 
                    218:     switch (offset) {
                    219:     case TCFG0: case TCFG1:
                    220:         index = (offset - TCFG0) >> 2;
                    221:         value = s->reg_tcfg[index];
                    222:         break;
                    223: 
                    224:     case TCON:
                    225:         value = s->reg_tcon;
                    226:         break;
                    227: 
                    228:     case TCNTB0: case TCNTB1:
                    229:     case TCNTB2: case TCNTB3: case TCNTB4:
                    230:         index = (offset - TCNTB0) / 0xC;
                    231:         value = s->timer[index].reg_tcntb;
                    232:         break;
                    233: 
                    234:     case TCMPB0: case TCMPB1:
                    235:     case TCMPB2: case TCMPB3:
                    236:         index = (offset - TCMPB0) / 0xC;
                    237:         value = s->timer[index].reg_tcmpb;
                    238:         break;
                    239: 
                    240:     case TCNTO0: case TCNTO1:
                    241:     case TCNTO2: case TCNTO3: case TCNTO4:
                    242:         index = (offset == TCNTO4) ? 4 : (offset - TCNTO0) / 0xC;
                    243:         value = ptimer_get_count(s->timer[index].ptimer);
                    244:         break;
                    245: 
                    246:     case TINT_CSTAT:
                    247:         value = s->reg_tint_cstat;
                    248:         break;
                    249: 
                    250:     default:
                    251:         fprintf(stderr,
                    252:                 "[exynos4210.pwm: bad read offset " TARGET_FMT_plx "]\n",
                    253:                 offset);
                    254:         break;
                    255:     }
                    256:     return value;
                    257: }
                    258: 
                    259: /*
                    260:  * PWM Write
                    261:  */
                    262: static void exynos4210_pwm_write(void *opaque, target_phys_addr_t offset,
                    263:         uint64_t value, unsigned size)
                    264: {
                    265:     Exynos4210PWMState *s = (Exynos4210PWMState *)opaque;
                    266:     int index;
                    267:     uint32_t new_val;
                    268:     int i;
                    269: 
                    270:     switch (offset) {
                    271:     case TCFG0: case TCFG1:
                    272:         index = (offset - TCFG0) >> 2;
                    273:         s->reg_tcfg[index] = value;
                    274: 
                    275:         /* update timers frequencies */
                    276:         for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) {
                    277:             exynos4210_pwm_update_freq(s, s->timer[i].id);
                    278:         }
                    279:         break;
                    280: 
                    281:     case TCON:
                    282:         for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) {
                    283:             if ((value & TCON_TIMER_MANUAL_UPD(i)) >
                    284:             (s->reg_tcon & TCON_TIMER_MANUAL_UPD(i))) {
                    285:                 /*
                    286:                  * TCNTB and TCMPB are loaded into TCNT and TCMP.
                    287:                  * Update timers.
                    288:                  */
                    289: 
                    290:                 /* this will start timer to run, this ok, because
                    291:                  * during processing start bit timer will be stopped
                    292:                  * if needed */
                    293:                 ptimer_set_count(s->timer[i].ptimer, s->timer[i].reg_tcntb);
                    294:                 DPRINTF("set timer %d count to %x\n", i,
                    295:                         s->timer[i].reg_tcntb);
                    296:             }
                    297: 
                    298:             if ((value & TCON_TIMER_START(i)) >
                    299:             (s->reg_tcon & TCON_TIMER_START(i))) {
                    300:                 /* changed to start */
                    301:                 ptimer_run(s->timer[i].ptimer, 1);
                    302:                 DPRINTF("run timer %d\n", i);
                    303:             }
                    304: 
                    305:             if ((value & TCON_TIMER_START(i)) <
                    306:                     (s->reg_tcon & TCON_TIMER_START(i))) {
                    307:                 /* changed to stop */
                    308:                 ptimer_stop(s->timer[i].ptimer);
                    309:                 DPRINTF("stop timer %d\n", i);
                    310:             }
                    311:         }
                    312:         s->reg_tcon = value;
                    313:         break;
                    314: 
                    315:     case TCNTB0: case TCNTB1:
                    316:     case TCNTB2: case TCNTB3: case TCNTB4:
                    317:         index = (offset - TCNTB0) / 0xC;
                    318:         s->timer[index].reg_tcntb = value;
                    319:         break;
                    320: 
                    321:     case TCMPB0: case TCMPB1:
                    322:     case TCMPB2: case TCMPB3:
                    323:         index = (offset - TCMPB0) / 0xC;
                    324:         s->timer[index].reg_tcmpb = value;
                    325:         break;
                    326: 
                    327:     case TINT_CSTAT:
                    328:         new_val = (s->reg_tint_cstat & 0x3E0) + (0x1F & value);
                    329:         new_val &= ~(0x3E0 & value);
                    330: 
                    331:         for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) {
                    332:             if ((new_val & TINT_CSTAT_STATUS(i)) <
                    333:                     (s->reg_tint_cstat & TINT_CSTAT_STATUS(i))) {
                    334:                 qemu_irq_lower(s->timer[i].irq);
                    335:             }
                    336:         }
                    337: 
                    338:         s->reg_tint_cstat = new_val;
                    339:         break;
                    340: 
                    341:     default:
                    342:         fprintf(stderr,
                    343:                 "[exynos4210.pwm: bad write offset " TARGET_FMT_plx "]\n",
                    344:                 offset);
                    345:         break;
                    346: 
                    347:     }
                    348: }
                    349: 
                    350: /*
                    351:  * Set default values to timer fields and registers
                    352:  */
                    353: static void exynos4210_pwm_reset(DeviceState *d)
                    354: {
                    355:     Exynos4210PWMState *s = (Exynos4210PWMState *)d;
                    356:     int i;
                    357:     s->reg_tcfg[0] = 0x0101;
                    358:     s->reg_tcfg[1] = 0x0;
                    359:     s->reg_tcon = 0;
                    360:     s->reg_tint_cstat = 0;
                    361:     for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) {
                    362:         s->timer[i].reg_tcmpb = 0;
                    363:         s->timer[i].reg_tcntb = 0;
                    364: 
                    365:         exynos4210_pwm_update_freq(s, s->timer[i].id);
                    366:         ptimer_stop(s->timer[i].ptimer);
                    367:     }
                    368: }
                    369: 
                    370: static const MemoryRegionOps exynos4210_pwm_ops = {
                    371:     .read = exynos4210_pwm_read,
                    372:     .write = exynos4210_pwm_write,
                    373:     .endianness = DEVICE_NATIVE_ENDIAN,
                    374: };
                    375: 
                    376: /*
                    377:  * PWM timer initialization
                    378:  */
                    379: static int exynos4210_pwm_init(SysBusDevice *dev)
                    380: {
                    381:     Exynos4210PWMState *s = FROM_SYSBUS(Exynos4210PWMState, dev);
                    382:     int i;
                    383:     QEMUBH *bh;
                    384: 
                    385:     for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) {
                    386:         bh = qemu_bh_new(exynos4210_pwm_tick, &s->timer[i]);
                    387:         sysbus_init_irq(dev, &s->timer[i].irq);
                    388:         s->timer[i].ptimer = ptimer_init(bh);
                    389:         s->timer[i].id = i;
                    390:         s->timer[i].parent = s;
                    391:     }
                    392: 
                    393:     memory_region_init_io(&s->iomem, &exynos4210_pwm_ops, s, "exynos4210-pwm",
                    394:             EXYNOS4210_PWM_REG_MEM_SIZE);
                    395:     sysbus_init_mmio(dev, &s->iomem);
                    396: 
                    397:     return 0;
                    398: }
                    399: 
                    400: static void exynos4210_pwm_class_init(ObjectClass *klass, void *data)
                    401: {
                    402:     DeviceClass *dc = DEVICE_CLASS(klass);
                    403:     SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
                    404: 
                    405:     k->init = exynos4210_pwm_init;
                    406:     dc->reset = exynos4210_pwm_reset;
                    407:     dc->vmsd = &vmstate_exynos4210_pwm_state;
                    408: }
                    409: 
                    410: static TypeInfo exynos4210_pwm_info = {
                    411:     .name          = "exynos4210.pwm",
                    412:     .parent        = TYPE_SYS_BUS_DEVICE,
                    413:     .instance_size = sizeof(Exynos4210PWMState),
                    414:     .class_init    = exynos4210_pwm_class_init,
                    415: };
                    416: 
                    417: static void exynos4210_pwm_register_types(void)
                    418: {
                    419:     type_register_static(&exynos4210_pwm_info);
                    420: }
                    421: 
                    422: type_init(exynos4210_pwm_register_types)

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.