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