13bd88451SPaolo Bonzini /*
23bd88451SPaolo Bonzini * Samsung exynos4210 Pulse Width Modulation Timer
33bd88451SPaolo Bonzini *
43bd88451SPaolo Bonzini * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd.
53bd88451SPaolo Bonzini * All rights reserved.
63bd88451SPaolo Bonzini *
73bd88451SPaolo Bonzini * Evgeny Voevodin <e.voevodin@samsung.com>
83bd88451SPaolo Bonzini *
93bd88451SPaolo Bonzini * This program is free software; you can redistribute it and/or modify it
103bd88451SPaolo Bonzini * under the terms of the GNU General Public License as published by the
113bd88451SPaolo Bonzini * Free Software Foundation; either version 2 of the License, or (at your
123bd88451SPaolo Bonzini * option) any later version.
133bd88451SPaolo Bonzini *
143bd88451SPaolo Bonzini * This program is distributed in the hope that it will be useful,
153bd88451SPaolo Bonzini * but WITHOUT ANY WARRANTY; without even the implied warranty of
163bd88451SPaolo Bonzini * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
173bd88451SPaolo Bonzini * See the GNU General Public License for more details.
183bd88451SPaolo Bonzini *
193bd88451SPaolo Bonzini * You should have received a copy of the GNU General Public License along
203bd88451SPaolo Bonzini * with this program; if not, see <http://www.gnu.org/licenses/>.
213bd88451SPaolo Bonzini */
223bd88451SPaolo Bonzini
238ef94f0bSPeter Maydell #include "qemu/osdep.h"
24f2ad5140SKrzysztof Kozlowski #include "qemu/log.h"
253bd88451SPaolo Bonzini #include "hw/sysbus.h"
26d6454270SMarkus Armbruster #include "migration/vmstate.h"
273bd88451SPaolo Bonzini #include "qemu/timer.h"
280b8fa32fSMarkus Armbruster #include "qemu/module.h"
293bd88451SPaolo Bonzini #include "hw/ptimer.h"
303bd88451SPaolo Bonzini
313bd88451SPaolo Bonzini #include "hw/arm/exynos4210.h"
3264552b6bSMarkus Armbruster #include "hw/irq.h"
33db1015e9SEduardo Habkost #include "qom/object.h"
343bd88451SPaolo Bonzini
353bd88451SPaolo Bonzini //#define DEBUG_PWM
363bd88451SPaolo Bonzini
373bd88451SPaolo Bonzini #ifdef DEBUG_PWM
383bd88451SPaolo Bonzini #define DPRINTF(fmt, ...) \
393bd88451SPaolo Bonzini do { fprintf(stdout, "PWM: [%24s:%5d] " fmt, __func__, __LINE__, \
403bd88451SPaolo Bonzini ## __VA_ARGS__); } while (0)
413bd88451SPaolo Bonzini #else
423bd88451SPaolo Bonzini #define DPRINTF(fmt, ...) do {} while (0)
433bd88451SPaolo Bonzini #endif
443bd88451SPaolo Bonzini
453bd88451SPaolo Bonzini #define EXYNOS4210_PWM_TIMERS_NUM 5
463bd88451SPaolo Bonzini #define EXYNOS4210_PWM_REG_MEM_SIZE 0x50
473bd88451SPaolo Bonzini
483bd88451SPaolo Bonzini #define TCFG0 0x0000
493bd88451SPaolo Bonzini #define TCFG1 0x0004
503bd88451SPaolo Bonzini #define TCON 0x0008
513bd88451SPaolo Bonzini #define TCNTB0 0x000C
523bd88451SPaolo Bonzini #define TCMPB0 0x0010
533bd88451SPaolo Bonzini #define TCNTO0 0x0014
543bd88451SPaolo Bonzini #define TCNTB1 0x0018
553bd88451SPaolo Bonzini #define TCMPB1 0x001C
563bd88451SPaolo Bonzini #define TCNTO1 0x0020
573bd88451SPaolo Bonzini #define TCNTB2 0x0024
583bd88451SPaolo Bonzini #define TCMPB2 0x0028
593bd88451SPaolo Bonzini #define TCNTO2 0x002C
603bd88451SPaolo Bonzini #define TCNTB3 0x0030
613bd88451SPaolo Bonzini #define TCMPB3 0x0034
623bd88451SPaolo Bonzini #define TCNTO3 0x0038
633bd88451SPaolo Bonzini #define TCNTB4 0x003C
643bd88451SPaolo Bonzini #define TCNTO4 0x0040
653bd88451SPaolo Bonzini #define TINT_CSTAT 0x0044
663bd88451SPaolo Bonzini
673bd88451SPaolo Bonzini #define TCNTB(x) (0xC * (x))
683bd88451SPaolo Bonzini #define TCMPB(x) (0xC * (x) + 1)
693bd88451SPaolo Bonzini #define TCNTO(x) (0xC * (x) + 2)
703bd88451SPaolo Bonzini
713bd88451SPaolo Bonzini #define GET_PRESCALER(reg, x) (((reg) & (0xFF << (8 * (x)))) >> 8 * (x))
723bd88451SPaolo Bonzini #define GET_DIVIDER(reg, x) (1 << (((reg) & (0xF << (4 * (x)))) >> (4 * (x))))
733bd88451SPaolo Bonzini
743bd88451SPaolo Bonzini /*
753bd88451SPaolo Bonzini * Attention! Timer4 doesn't have OUTPUT_INVERTER,
763bd88451SPaolo Bonzini * so Auto Reload bit is not accessible by macros!
773bd88451SPaolo Bonzini */
783bd88451SPaolo Bonzini #define TCON_TIMER_BASE(x) (((x) ? 1 : 0) * 4 + 4 * (x))
793bd88451SPaolo Bonzini #define TCON_TIMER_START(x) (1 << (TCON_TIMER_BASE(x) + 0))
803bd88451SPaolo Bonzini #define TCON_TIMER_MANUAL_UPD(x) (1 << (TCON_TIMER_BASE(x) + 1))
813bd88451SPaolo Bonzini #define TCON_TIMER_OUTPUT_INV(x) (1 << (TCON_TIMER_BASE(x) + 2))
823bd88451SPaolo Bonzini #define TCON_TIMER_AUTO_RELOAD(x) (1 << (TCON_TIMER_BASE(x) + 3))
833bd88451SPaolo Bonzini #define TCON_TIMER4_AUTO_RELOAD (1 << 22)
843bd88451SPaolo Bonzini
853bd88451SPaolo Bonzini #define TINT_CSTAT_STATUS(x) (1 << (5 + (x)))
863bd88451SPaolo Bonzini #define TINT_CSTAT_ENABLE(x) (1 << (x))
873bd88451SPaolo Bonzini
883bd88451SPaolo Bonzini /* timer struct */
893bd88451SPaolo Bonzini typedef struct {
903bd88451SPaolo Bonzini uint32_t id; /* timer id */
913bd88451SPaolo Bonzini qemu_irq irq; /* local timer irq */
923bd88451SPaolo Bonzini uint32_t freq; /* timer frequency */
933bd88451SPaolo Bonzini
943bd88451SPaolo Bonzini /* use ptimer.c to represent count down timer */
953bd88451SPaolo Bonzini ptimer_state *ptimer; /* timer */
963bd88451SPaolo Bonzini
973bd88451SPaolo Bonzini /* registers */
983bd88451SPaolo Bonzini uint32_t reg_tcntb; /* counter register buffer */
993bd88451SPaolo Bonzini uint32_t reg_tcmpb; /* compare register buffer */
1003bd88451SPaolo Bonzini
1013bd88451SPaolo Bonzini struct Exynos4210PWMState *parent;
1023bd88451SPaolo Bonzini
1033bd88451SPaolo Bonzini } Exynos4210PWM;
1043bd88451SPaolo Bonzini
10525fce9adSAndreas Färber #define TYPE_EXYNOS4210_PWM "exynos4210.pwm"
1068063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(Exynos4210PWMState, EXYNOS4210_PWM)
1073bd88451SPaolo Bonzini
108db1015e9SEduardo Habkost struct Exynos4210PWMState {
10925fce9adSAndreas Färber SysBusDevice parent_obj;
11025fce9adSAndreas Färber
1113bd88451SPaolo Bonzini MemoryRegion iomem;
1123bd88451SPaolo Bonzini
1133bd88451SPaolo Bonzini uint32_t reg_tcfg[2];
1143bd88451SPaolo Bonzini uint32_t reg_tcon;
1153bd88451SPaolo Bonzini uint32_t reg_tint_cstat;
1163bd88451SPaolo Bonzini
1173bd88451SPaolo Bonzini Exynos4210PWM timer[EXYNOS4210_PWM_TIMERS_NUM];
1183bd88451SPaolo Bonzini
119db1015e9SEduardo Habkost };
1203bd88451SPaolo Bonzini
1213bd88451SPaolo Bonzini /*** VMState ***/
1223bd88451SPaolo Bonzini static const VMStateDescription vmstate_exynos4210_pwm = {
1233bd88451SPaolo Bonzini .name = "exynos4210.pwm.pwm",
1243bd88451SPaolo Bonzini .version_id = 1,
1253bd88451SPaolo Bonzini .minimum_version_id = 1,
126ba324b3fSRichard Henderson .fields = (const VMStateField[]) {
1273bd88451SPaolo Bonzini VMSTATE_UINT32(id, Exynos4210PWM),
1283bd88451SPaolo Bonzini VMSTATE_UINT32(freq, Exynos4210PWM),
1293bd88451SPaolo Bonzini VMSTATE_PTIMER(ptimer, Exynos4210PWM),
1303bd88451SPaolo Bonzini VMSTATE_UINT32(reg_tcntb, Exynos4210PWM),
1313bd88451SPaolo Bonzini VMSTATE_UINT32(reg_tcmpb, Exynos4210PWM),
1323bd88451SPaolo Bonzini VMSTATE_END_OF_LIST()
1333bd88451SPaolo Bonzini }
1343bd88451SPaolo Bonzini };
1353bd88451SPaolo Bonzini
1363bd88451SPaolo Bonzini static const VMStateDescription vmstate_exynos4210_pwm_state = {
1373bd88451SPaolo Bonzini .name = "exynos4210.pwm",
1383bd88451SPaolo Bonzini .version_id = 1,
1393bd88451SPaolo Bonzini .minimum_version_id = 1,
140ba324b3fSRichard Henderson .fields = (const VMStateField[]) {
1413bd88451SPaolo Bonzini VMSTATE_UINT32_ARRAY(reg_tcfg, Exynos4210PWMState, 2),
1423bd88451SPaolo Bonzini VMSTATE_UINT32(reg_tcon, Exynos4210PWMState),
1433bd88451SPaolo Bonzini VMSTATE_UINT32(reg_tint_cstat, Exynos4210PWMState),
1443bd88451SPaolo Bonzini VMSTATE_STRUCT_ARRAY(timer, Exynos4210PWMState,
1453bd88451SPaolo Bonzini EXYNOS4210_PWM_TIMERS_NUM, 0,
1463bd88451SPaolo Bonzini vmstate_exynos4210_pwm, Exynos4210PWM),
1473bd88451SPaolo Bonzini VMSTATE_END_OF_LIST()
1483bd88451SPaolo Bonzini }
1493bd88451SPaolo Bonzini };
1503bd88451SPaolo Bonzini
1513bd88451SPaolo Bonzini /*
152b1b104edSPeter Maydell * PWM update frequency.
153b1b104edSPeter Maydell * Must be called within a ptimer_transaction_begin/commit block
154b1b104edSPeter Maydell * for s->timer[id].ptimer.
1553bd88451SPaolo Bonzini */
exynos4210_pwm_update_freq(Exynos4210PWMState * s,uint32_t id)1563bd88451SPaolo Bonzini static void exynos4210_pwm_update_freq(Exynos4210PWMState *s, uint32_t id)
1573bd88451SPaolo Bonzini {
1583bd88451SPaolo Bonzini uint32_t freq;
1593bd88451SPaolo Bonzini freq = s->timer[id].freq;
1603bd88451SPaolo Bonzini if (id > 1) {
1613bd88451SPaolo Bonzini s->timer[id].freq = 24000000 /
1623bd88451SPaolo Bonzini ((GET_PRESCALER(s->reg_tcfg[0], 1) + 1) *
1633bd88451SPaolo Bonzini (GET_DIVIDER(s->reg_tcfg[1], id)));
1643bd88451SPaolo Bonzini } else {
1653bd88451SPaolo Bonzini s->timer[id].freq = 24000000 /
1663bd88451SPaolo Bonzini ((GET_PRESCALER(s->reg_tcfg[0], 0) + 1) *
1673bd88451SPaolo Bonzini (GET_DIVIDER(s->reg_tcfg[1], id)));
1683bd88451SPaolo Bonzini }
1693bd88451SPaolo Bonzini
1703bd88451SPaolo Bonzini if (freq != s->timer[id].freq) {
1713bd88451SPaolo Bonzini ptimer_set_freq(s->timer[id].ptimer, s->timer[id].freq);
17263192565SAlex Chen DPRINTF("freq=%uHz\n", s->timer[id].freq);
1733bd88451SPaolo Bonzini }
1743bd88451SPaolo Bonzini }
1753bd88451SPaolo Bonzini
1763bd88451SPaolo Bonzini /*
1773bd88451SPaolo Bonzini * Counter tick handler
1783bd88451SPaolo Bonzini */
exynos4210_pwm_tick(void * opaque)1793bd88451SPaolo Bonzini static void exynos4210_pwm_tick(void *opaque)
1803bd88451SPaolo Bonzini {
1813bd88451SPaolo Bonzini Exynos4210PWM *s = (Exynos4210PWM *)opaque;
1823bd88451SPaolo Bonzini Exynos4210PWMState *p = (Exynos4210PWMState *)s->parent;
1833bd88451SPaolo Bonzini uint32_t id = s->id;
1843bd88451SPaolo Bonzini bool cmp;
1853bd88451SPaolo Bonzini
18663192565SAlex Chen DPRINTF("timer %u tick\n", id);
1873bd88451SPaolo Bonzini
1883bd88451SPaolo Bonzini /* set irq status */
1893bd88451SPaolo Bonzini p->reg_tint_cstat |= TINT_CSTAT_STATUS(id);
1903bd88451SPaolo Bonzini
1913bd88451SPaolo Bonzini /* raise IRQ */
1923bd88451SPaolo Bonzini if (p->reg_tint_cstat & TINT_CSTAT_ENABLE(id)) {
19363192565SAlex Chen DPRINTF("timer %u IRQ\n", id);
1943bd88451SPaolo Bonzini qemu_irq_raise(p->timer[id].irq);
1953bd88451SPaolo Bonzini }
1963bd88451SPaolo Bonzini
1973bd88451SPaolo Bonzini /* reload timer */
1983bd88451SPaolo Bonzini if (id != 4) {
1993bd88451SPaolo Bonzini cmp = p->reg_tcon & TCON_TIMER_AUTO_RELOAD(id);
2003bd88451SPaolo Bonzini } else {
2013bd88451SPaolo Bonzini cmp = p->reg_tcon & TCON_TIMER4_AUTO_RELOAD;
2023bd88451SPaolo Bonzini }
2033bd88451SPaolo Bonzini
2043bd88451SPaolo Bonzini if (cmp) {
20563192565SAlex Chen DPRINTF("auto reload timer %u count to %x\n", id,
2063bd88451SPaolo Bonzini p->timer[id].reg_tcntb);
2073bd88451SPaolo Bonzini ptimer_set_count(p->timer[id].ptimer, p->timer[id].reg_tcntb);
2083bd88451SPaolo Bonzini ptimer_run(p->timer[id].ptimer, 1);
2093bd88451SPaolo Bonzini } else {
2103bd88451SPaolo Bonzini /* stop timer, set status to STOP, see Basic Timer Operation */
2113bd88451SPaolo Bonzini p->reg_tcon &= ~TCON_TIMER_START(id);
2123bd88451SPaolo Bonzini ptimer_stop(p->timer[id].ptimer);
2133bd88451SPaolo Bonzini }
2143bd88451SPaolo Bonzini }
2153bd88451SPaolo Bonzini
2163bd88451SPaolo Bonzini /*
2173bd88451SPaolo Bonzini * PWM Read
2183bd88451SPaolo Bonzini */
exynos4210_pwm_read(void * opaque,hwaddr offset,unsigned size)2193bd88451SPaolo Bonzini static uint64_t exynos4210_pwm_read(void *opaque, hwaddr offset,
2203bd88451SPaolo Bonzini unsigned size)
2213bd88451SPaolo Bonzini {
2223bd88451SPaolo Bonzini Exynos4210PWMState *s = (Exynos4210PWMState *)opaque;
2233bd88451SPaolo Bonzini uint32_t value = 0;
2243bd88451SPaolo Bonzini int index;
2253bd88451SPaolo Bonzini
2263bd88451SPaolo Bonzini switch (offset) {
2273bd88451SPaolo Bonzini case TCFG0: case TCFG1:
2283bd88451SPaolo Bonzini index = (offset - TCFG0) >> 2;
2293bd88451SPaolo Bonzini value = s->reg_tcfg[index];
2303bd88451SPaolo Bonzini break;
2313bd88451SPaolo Bonzini
2323bd88451SPaolo Bonzini case TCON:
2333bd88451SPaolo Bonzini value = s->reg_tcon;
2343bd88451SPaolo Bonzini break;
2353bd88451SPaolo Bonzini
2363bd88451SPaolo Bonzini case TCNTB0: case TCNTB1:
2373bd88451SPaolo Bonzini case TCNTB2: case TCNTB3: case TCNTB4:
2383bd88451SPaolo Bonzini index = (offset - TCNTB0) / 0xC;
2393bd88451SPaolo Bonzini value = s->timer[index].reg_tcntb;
2403bd88451SPaolo Bonzini break;
2413bd88451SPaolo Bonzini
2423bd88451SPaolo Bonzini case TCMPB0: case TCMPB1:
2433bd88451SPaolo Bonzini case TCMPB2: case TCMPB3:
2443bd88451SPaolo Bonzini index = (offset - TCMPB0) / 0xC;
2453bd88451SPaolo Bonzini value = s->timer[index].reg_tcmpb;
2463bd88451SPaolo Bonzini break;
2473bd88451SPaolo Bonzini
2483bd88451SPaolo Bonzini case TCNTO0: case TCNTO1:
2493bd88451SPaolo Bonzini case TCNTO2: case TCNTO3: case TCNTO4:
2503bd88451SPaolo Bonzini index = (offset == TCNTO4) ? 4 : (offset - TCNTO0) / 0xC;
2513bd88451SPaolo Bonzini value = ptimer_get_count(s->timer[index].ptimer);
2523bd88451SPaolo Bonzini break;
2533bd88451SPaolo Bonzini
2543bd88451SPaolo Bonzini case TINT_CSTAT:
2553bd88451SPaolo Bonzini value = s->reg_tint_cstat;
2563bd88451SPaolo Bonzini break;
2573bd88451SPaolo Bonzini
2583bd88451SPaolo Bonzini default:
259f2ad5140SKrzysztof Kozlowski qemu_log_mask(LOG_GUEST_ERROR,
260883f2c59SPhilippe Mathieu-Daudé "exynos4210.pwm: bad read offset " HWADDR_FMT_plx,
2613bd88451SPaolo Bonzini offset);
2623bd88451SPaolo Bonzini break;
2633bd88451SPaolo Bonzini }
2643bd88451SPaolo Bonzini return value;
2653bd88451SPaolo Bonzini }
2663bd88451SPaolo Bonzini
2673bd88451SPaolo Bonzini /*
2683bd88451SPaolo Bonzini * PWM Write
2693bd88451SPaolo Bonzini */
exynos4210_pwm_write(void * opaque,hwaddr offset,uint64_t value,unsigned size)2703bd88451SPaolo Bonzini static void exynos4210_pwm_write(void *opaque, hwaddr offset,
2713bd88451SPaolo Bonzini uint64_t value, unsigned size)
2723bd88451SPaolo Bonzini {
2733bd88451SPaolo Bonzini Exynos4210PWMState *s = (Exynos4210PWMState *)opaque;
2743bd88451SPaolo Bonzini int index;
2753bd88451SPaolo Bonzini uint32_t new_val;
2763bd88451SPaolo Bonzini int i;
2773bd88451SPaolo Bonzini
2783bd88451SPaolo Bonzini switch (offset) {
2793bd88451SPaolo Bonzini case TCFG0: case TCFG1:
2803bd88451SPaolo Bonzini index = (offset - TCFG0) >> 2;
2813bd88451SPaolo Bonzini s->reg_tcfg[index] = value;
2823bd88451SPaolo Bonzini
2833bd88451SPaolo Bonzini /* update timers frequencies */
2843bd88451SPaolo Bonzini for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) {
285b1b104edSPeter Maydell ptimer_transaction_begin(s->timer[i].ptimer);
2863bd88451SPaolo Bonzini exynos4210_pwm_update_freq(s, s->timer[i].id);
287b1b104edSPeter Maydell ptimer_transaction_commit(s->timer[i].ptimer);
2883bd88451SPaolo Bonzini }
2893bd88451SPaolo Bonzini break;
2903bd88451SPaolo Bonzini
2913bd88451SPaolo Bonzini case TCON:
2923bd88451SPaolo Bonzini for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) {
293b1b104edSPeter Maydell ptimer_transaction_begin(s->timer[i].ptimer);
2943bd88451SPaolo Bonzini if ((value & TCON_TIMER_MANUAL_UPD(i)) >
2953bd88451SPaolo Bonzini (s->reg_tcon & TCON_TIMER_MANUAL_UPD(i))) {
2963bd88451SPaolo Bonzini /*
2973bd88451SPaolo Bonzini * TCNTB and TCMPB are loaded into TCNT and TCMP.
2983bd88451SPaolo Bonzini * Update timers.
2993bd88451SPaolo Bonzini */
3003bd88451SPaolo Bonzini
3013bd88451SPaolo Bonzini /* this will start timer to run, this ok, because
3023bd88451SPaolo Bonzini * during processing start bit timer will be stopped
3033bd88451SPaolo Bonzini * if needed */
3043bd88451SPaolo Bonzini ptimer_set_count(s->timer[i].ptimer, s->timer[i].reg_tcntb);
3053bd88451SPaolo Bonzini DPRINTF("set timer %d count to %x\n", i,
3063bd88451SPaolo Bonzini s->timer[i].reg_tcntb);
3073bd88451SPaolo Bonzini }
3083bd88451SPaolo Bonzini
3093bd88451SPaolo Bonzini if ((value & TCON_TIMER_START(i)) >
3103bd88451SPaolo Bonzini (s->reg_tcon & TCON_TIMER_START(i))) {
3113bd88451SPaolo Bonzini /* changed to start */
3123bd88451SPaolo Bonzini ptimer_run(s->timer[i].ptimer, 1);
3133bd88451SPaolo Bonzini DPRINTF("run timer %d\n", i);
3143bd88451SPaolo Bonzini }
3153bd88451SPaolo Bonzini
3163bd88451SPaolo Bonzini if ((value & TCON_TIMER_START(i)) <
3173bd88451SPaolo Bonzini (s->reg_tcon & TCON_TIMER_START(i))) {
3183bd88451SPaolo Bonzini /* changed to stop */
3193bd88451SPaolo Bonzini ptimer_stop(s->timer[i].ptimer);
3203bd88451SPaolo Bonzini DPRINTF("stop timer %d\n", i);
3213bd88451SPaolo Bonzini }
322b1b104edSPeter Maydell ptimer_transaction_commit(s->timer[i].ptimer);
3233bd88451SPaolo Bonzini }
3243bd88451SPaolo Bonzini s->reg_tcon = value;
3253bd88451SPaolo Bonzini break;
3263bd88451SPaolo Bonzini
3273bd88451SPaolo Bonzini case TCNTB0: case TCNTB1:
3283bd88451SPaolo Bonzini case TCNTB2: case TCNTB3: case TCNTB4:
3293bd88451SPaolo Bonzini index = (offset - TCNTB0) / 0xC;
3303bd88451SPaolo Bonzini s->timer[index].reg_tcntb = value;
3313bd88451SPaolo Bonzini break;
3323bd88451SPaolo Bonzini
3333bd88451SPaolo Bonzini case TCMPB0: case TCMPB1:
3343bd88451SPaolo Bonzini case TCMPB2: case TCMPB3:
3353bd88451SPaolo Bonzini index = (offset - TCMPB0) / 0xC;
3363bd88451SPaolo Bonzini s->timer[index].reg_tcmpb = value;
3373bd88451SPaolo Bonzini break;
3383bd88451SPaolo Bonzini
3393bd88451SPaolo Bonzini case TINT_CSTAT:
3403bd88451SPaolo Bonzini new_val = (s->reg_tint_cstat & 0x3E0) + (0x1F & value);
3413bd88451SPaolo Bonzini new_val &= ~(0x3E0 & value);
3423bd88451SPaolo Bonzini
3433bd88451SPaolo Bonzini for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) {
3443bd88451SPaolo Bonzini if ((new_val & TINT_CSTAT_STATUS(i)) <
3453bd88451SPaolo Bonzini (s->reg_tint_cstat & TINT_CSTAT_STATUS(i))) {
3463bd88451SPaolo Bonzini qemu_irq_lower(s->timer[i].irq);
3473bd88451SPaolo Bonzini }
3483bd88451SPaolo Bonzini }
3493bd88451SPaolo Bonzini
3503bd88451SPaolo Bonzini s->reg_tint_cstat = new_val;
3513bd88451SPaolo Bonzini break;
3523bd88451SPaolo Bonzini
3533bd88451SPaolo Bonzini default:
354f2ad5140SKrzysztof Kozlowski qemu_log_mask(LOG_GUEST_ERROR,
355883f2c59SPhilippe Mathieu-Daudé "exynos4210.pwm: bad write offset " HWADDR_FMT_plx,
3563bd88451SPaolo Bonzini offset);
3573bd88451SPaolo Bonzini break;
3583bd88451SPaolo Bonzini
3593bd88451SPaolo Bonzini }
3603bd88451SPaolo Bonzini }
3613bd88451SPaolo Bonzini
3623bd88451SPaolo Bonzini /*
3633bd88451SPaolo Bonzini * Set default values to timer fields and registers
3643bd88451SPaolo Bonzini */
exynos4210_pwm_reset(DeviceState * d)3653bd88451SPaolo Bonzini static void exynos4210_pwm_reset(DeviceState *d)
3663bd88451SPaolo Bonzini {
36725fce9adSAndreas Färber Exynos4210PWMState *s = EXYNOS4210_PWM(d);
3683bd88451SPaolo Bonzini int i;
3693bd88451SPaolo Bonzini s->reg_tcfg[0] = 0x0101;
3703bd88451SPaolo Bonzini s->reg_tcfg[1] = 0x0;
3713bd88451SPaolo Bonzini s->reg_tcon = 0;
3723bd88451SPaolo Bonzini s->reg_tint_cstat = 0;
3733bd88451SPaolo Bonzini for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) {
3743bd88451SPaolo Bonzini s->timer[i].reg_tcmpb = 0;
3753bd88451SPaolo Bonzini s->timer[i].reg_tcntb = 0;
3763bd88451SPaolo Bonzini
377b1b104edSPeter Maydell ptimer_transaction_begin(s->timer[i].ptimer);
3783bd88451SPaolo Bonzini exynos4210_pwm_update_freq(s, s->timer[i].id);
3793bd88451SPaolo Bonzini ptimer_stop(s->timer[i].ptimer);
380b1b104edSPeter Maydell ptimer_transaction_commit(s->timer[i].ptimer);
3813bd88451SPaolo Bonzini }
3823bd88451SPaolo Bonzini }
3833bd88451SPaolo Bonzini
3843bd88451SPaolo Bonzini static const MemoryRegionOps exynos4210_pwm_ops = {
3853bd88451SPaolo Bonzini .read = exynos4210_pwm_read,
3863bd88451SPaolo Bonzini .write = exynos4210_pwm_write,
3873bd88451SPaolo Bonzini .endianness = DEVICE_NATIVE_ENDIAN,
3883bd88451SPaolo Bonzini };
3893bd88451SPaolo Bonzini
3903bd88451SPaolo Bonzini /*
3913bd88451SPaolo Bonzini * PWM timer initialization
3923bd88451SPaolo Bonzini */
exynos4210_pwm_init(Object * obj)393ff6ee495Sxiaoqiang.zhao static void exynos4210_pwm_init(Object *obj)
3943bd88451SPaolo Bonzini {
395ff6ee495Sxiaoqiang.zhao Exynos4210PWMState *s = EXYNOS4210_PWM(obj);
396ff6ee495Sxiaoqiang.zhao SysBusDevice *dev = SYS_BUS_DEVICE(obj);
3973bd88451SPaolo Bonzini int i;
3983bd88451SPaolo Bonzini
3993bd88451SPaolo Bonzini for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) {
4003bd88451SPaolo Bonzini sysbus_init_irq(dev, &s->timer[i].irq);
401b1b104edSPeter Maydell s->timer[i].ptimer = ptimer_init(exynos4210_pwm_tick,
402b1b104edSPeter Maydell &s->timer[i],
4039598c1bbSPeter Maydell PTIMER_POLICY_LEGACY);
4043bd88451SPaolo Bonzini s->timer[i].id = i;
4053bd88451SPaolo Bonzini s->timer[i].parent = s;
4063bd88451SPaolo Bonzini }
4073bd88451SPaolo Bonzini
408ff6ee495Sxiaoqiang.zhao memory_region_init_io(&s->iomem, obj, &exynos4210_pwm_ops, s,
409853dca12SPaolo Bonzini "exynos4210-pwm", EXYNOS4210_PWM_REG_MEM_SIZE);
4103bd88451SPaolo Bonzini sysbus_init_mmio(dev, &s->iomem);
4113bd88451SPaolo Bonzini }
4123bd88451SPaolo Bonzini
exynos4210_pwm_finalize(Object * obj)413c9342c09SGan Qixin static void exynos4210_pwm_finalize(Object *obj)
414c9342c09SGan Qixin {
415c9342c09SGan Qixin Exynos4210PWMState *s = EXYNOS4210_PWM(obj);
416c9342c09SGan Qixin int i;
417c9342c09SGan Qixin
418c9342c09SGan Qixin for (i = 0; i < EXYNOS4210_PWM_TIMERS_NUM; i++) {
419c9342c09SGan Qixin ptimer_free(s->timer[i].ptimer);
420c9342c09SGan Qixin }
421c9342c09SGan Qixin }
422c9342c09SGan Qixin
exynos4210_pwm_class_init(ObjectClass * klass,void * data)4233bd88451SPaolo Bonzini static void exynos4210_pwm_class_init(ObjectClass *klass, void *data)
4243bd88451SPaolo Bonzini {
4253bd88451SPaolo Bonzini DeviceClass *dc = DEVICE_CLASS(klass);
4263bd88451SPaolo Bonzini
427*e3d08143SPeter Maydell device_class_set_legacy_reset(dc, exynos4210_pwm_reset);
4283bd88451SPaolo Bonzini dc->vmsd = &vmstate_exynos4210_pwm_state;
4293bd88451SPaolo Bonzini }
4303bd88451SPaolo Bonzini
4313bd88451SPaolo Bonzini static const TypeInfo exynos4210_pwm_info = {
43225fce9adSAndreas Färber .name = TYPE_EXYNOS4210_PWM,
4333bd88451SPaolo Bonzini .parent = TYPE_SYS_BUS_DEVICE,
4343bd88451SPaolo Bonzini .instance_size = sizeof(Exynos4210PWMState),
435ff6ee495Sxiaoqiang.zhao .instance_init = exynos4210_pwm_init,
436c9342c09SGan Qixin .instance_finalize = exynos4210_pwm_finalize,
4373bd88451SPaolo Bonzini .class_init = exynos4210_pwm_class_init,
4383bd88451SPaolo Bonzini };
4393bd88451SPaolo Bonzini
exynos4210_pwm_register_types(void)4403bd88451SPaolo Bonzini static void exynos4210_pwm_register_types(void)
4413bd88451SPaolo Bonzini {
4423bd88451SPaolo Bonzini type_register_static(&exynos4210_pwm_info);
4433bd88451SPaolo Bonzini }
4443bd88451SPaolo Bonzini
4453bd88451SPaolo Bonzini type_init(exynos4210_pwm_register_types)
446