Annotation of qemu/hw/exynos4210_pwm.c, revision 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.