1
From 70fcbbed0057225cb301a3d2ae1d8d1889f60195 Mon Sep 17 00:00:00 2001
2
From: Peter Maydell <peter.maydell@linaro.org>
3
Date: Mon, 18 Feb 2013 16:58:31 +0000
4
Subject: [PATCH 49/77] Add triton2 (twl4030) driver
6
Content-Type: text/plain; charset=UTF-8
7
Content-Transfer-Encoding: 8bit
9
The swiss knife companion chip for omap3
11
includes change from Loïc Minier <lool@dooz.org>:
12
twl4030: Add PWMB OFF and ON regs
13
twl4030: add SW2 conversion channels
14
includes change from PMM: add dummy BCISIHCTRL
15
includes change from Riku: add carkit analog control registers
16
includes change from Riku: add rtc alarm and periodic interrupts
17
includes: twl4030: add callback for providing madc results
19
Signed-off-by: Juha Riihimäki <juha.riihimaki@nokia.com>
20
Signed-off-by: Riku Voipo <riku.voipio@nokia.com>
21
Signed-off-by: Loïc Minier <lool@dooz.org>
23
hw/arm/Makefile.objs | 1 +
25
hw/twl4030.c | 1808 ++++++++++++++++++++++++++++++++++++++++++++++++++
26
3 files changed, 1828 insertions(+)
27
create mode 100644 hw/twl4030.c
29
diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
30
index b8bc7aa..3e9cfd7 100644
31
--- a/hw/arm/Makefile.objs
32
+++ b/hw/arm/Makefile.objs
33
@@ -20,6 +20,7 @@ obj-y += omap1.o omap_lcdc.o omap_dma.o omap_clk.o omap_mmc.o omap_i2c.o \
34
obj-y += omap2.o omap_dss.o soc_dma.o omap_gptimer.o omap_synctimer.o \
35
omap_gpmc.o omap_sdrc.o omap_spi.o omap_tap.o omap_l4.o
36
obj-y += omap3.o omap_usb.o omap3_boot.o omap3_mmc.o dsi.o
38
obj-y += omap_sx1.o palm.o tsc210x.o
39
obj-y += nseries.o blizzard.o onenand.o cbus.o tusb6010.o usb/hcd-musb.o
40
obj-y += mst_fpga.o mainstone.o
41
diff --git a/hw/i2c.h b/hw/i2c.h
42
index 0e80d5a..0eba883 100644
45
@@ -75,6 +75,25 @@ void wm8750_set_bclk_in(void *opaque, int new_hz);
47
void lm832x_key_event(DeviceState *dev, int key, int state);
55
+typedef enum twl4030_adc_type {
60
+typedef uint16_t (*twl4030_madc_callback)(twl4030_adc_type type, int ch);
61
+void *twl4030_init(i2c_bus *bus, qemu_irq irq1, qemu_irq irq2,
62
+ const TWL4030KeyMap *keymap);
63
+void *twl5031_init(i2c_bus *bus, qemu_irq irq1, qemu_irq irq2,
64
+ const TWL4030KeyMap *keymap);
65
+void twl4030_set_powerbutton_state(void *opaque, int pressed);
66
+void twl4030_madc_attach(void *opaque, twl4030_madc_callback cb);
68
extern const VMStateDescription vmstate_i2c_slave;
70
#define VMSTATE_I2C_SLAVE(_field, _state) { \
71
diff --git a/hw/twl4030.c b/hw/twl4030.c
73
index 0000000..27e19e9
78
+ * TI TWL4030 emulation
80
+ * Copyright (C) 2008 yajin<yajin@vm-kernel.org>
81
+ * Copyright (C) 2009-2010 Nokia Corporation
83
+ * Register implementation based on TPS65950 ES1.0 specification.
85
+ * This program is free software; you can redistribute it and/or
86
+ * modify it under the terms of the GNU General Public License as
87
+ * published by the Free Software Foundation; either version 2 or
88
+ * (at your option) version 3 of the License.
90
+ * This program is distributed in the hope that it will be useful,
91
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
92
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
93
+ * GNU General Public License for more details.
95
+ * You should have received a copy of the GNU General Public License
96
+ * along with this program; if not, write to the Free Software
97
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
101
+#include <sys/time.h>
103
+#include "qemu/timer.h"
105
+#include "sysemu/sysemu.h"
106
+#include "ui/console.h"
107
+#include "exec/cpu-all.h"
109
+//#define DEBUG_GENERAL
112
+#define DEBUG_TRACE(fmt, ...) fprintf(stderr, "%s@%d: " fmt "\n", \
113
+ __FUNCTION__, __LINE__, ##__VA_ARGS__)
115
+#ifdef DEBUG_GENERAL
116
+#define TRACE(...) DEBUG_TRACE(__VA_ARGS__)
122
+#define TRACE_RTC(...) DEBUG_TRACE(__VA_ARGS__)
124
+#define TRACE_RTC(...)
127
+typedef struct TWL4030State TWL4030State;
128
+typedef struct TWL4030NodeState TWL4030NodeState;
130
+typedef uint8_t (*twl4030_read_func)(TWL4030NodeState *s,
132
+typedef void (*twl4030_write_func)(TWL4030NodeState *s,
133
+ uint8_t addr, uint8_t value);
135
+struct TWL4030NodeState {
140
+ twl4030_read_func read_func;
141
+ twl4030_write_func write_func;
142
+ TWL4030State *twl4030;
144
+ uint8 reg_data[256];
147
+struct TWL4030State {
150
+ QEMUTimer *alarm_timer;
151
+ QEMUTimer *periodic_timer;
152
+ const TWL4030KeyMap *keymap;
155
+ uint8_t twl5031_aciid;
156
+ twl4030_madc_callback madc_cb;
161
+ TWL4030NodeState *i2c[4];
163
+ uint8_t seq_mem[64][4]; /* power-management sequencing memory */
166
+static const uint8_t addr_48_reset_values[256] = {
167
+ 0x51, 0x04, 0x02, 0xc0, 0x41, 0x41, 0x41, 0x10, /* 0x00...0x07 */
168
+ 0x10, 0x10, 0x06, 0x06, 0x06, 0x1f, 0x1f, 0x1f, /* 0x08...0x0f */
169
+ 0x1f, 0x1f, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10...0x17 */
170
+ 0x00, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, /* 0x18...0x1f */
171
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0a, 0x03, /* 0x20...0x27 */
172
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28...0x2f */
173
+ 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x00, 0x00, /* 0x30...0x37 */
174
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38...0x3f */
175
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40...0x47 */
176
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48...0x4f */
177
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50...0x57 */
178
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58...0x5f */
179
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60...0x67 */
180
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68...0x6f */
181
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70...0x77 */
182
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78...0x7f */
183
+ 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00, /* 0x80...0x87 */
184
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88...0x8f */
185
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90...0x97 */
186
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98...0x9f */
187
+ 0x00, 0x10, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, /* 0xa0...0xa7 */
188
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8...0xaf */
189
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0...0xb7 */
190
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8...0xb8 */
191
+ 0xa0, 0xa0, 0x64, 0x7f, 0x6c, 0x75, 0x64, 0x20, /* 0xc0...0xc7 */
192
+ 0x01, 0x17, 0x01, 0x02, 0x00, 0x36, 0x44, 0x07, /* 0xc8...0xcf */
193
+ 0x3b, 0x17, 0x6b, 0x04, 0x00, 0x00, 0x00, 0x00, /* 0xd0...0xd7 */
194
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8...0xdf */
195
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0...0xe7 */
196
+ 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, /* 0xe8...0xef */
197
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0...0xf7 */
198
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00 /* 0xf8...0xff */
201
+static const uint8_t addr_49_reset_values[256] = {
202
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00...0x07 */
203
+ 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, /* 0x08...0x0f */
204
+ 0x3f, 0x3f, 0x3f, 0x3f, 0x25, 0x00, 0x00, 0x00, /* 0x10...0x17 */
205
+ 0x00, 0x32, 0x32, 0x32, 0x32, 0x00, 0x00, 0x55, /* 0x18...0x1f */
206
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20...0x27 */
207
+ 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, /* 0x28...0x2f */
208
+ 0x13, 0x00, 0x00, 0x00, 0x00, 0x79, 0x11, 0x00, /* 0x30...0x37 */
209
+ 0x00, 0x00, 0x06, 0x00, 0x44, 0x69, 0x00, 0x00, /* 0x38...0x3f */
210
+ 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, /* 0x40...0x47 */
211
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48...0x4f */
212
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50...0x57 */
213
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58...0x5f */
214
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60...0x67 */
215
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68...0x6f */
216
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70...0x77 */
217
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78...0x7f */
218
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80...0x87 */
219
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88...0x8f */
220
+ 0x00, 0x90, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, /* 0x90...0x97 */
221
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98...0x9f */
222
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0...0xa7 */
223
+ 0x00, 0x00, 0x04, 0x00, 0x55, 0x01, 0x55, 0x05, /* 0xa8...0xaf */
224
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x03, 0x00, /* 0xb0...0xb7 */
225
+ 0x00, 0x00, 0xff, 0xff, 0x03, 0x00, 0x00, 0x00, /* 0xb8...0xbf */
226
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, /* 0xc0...0xc7 */
227
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8...0xcf */
228
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0...0xd7 */
229
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8...0xdf */
230
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0...0xe7 */
231
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8...0xef */
232
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0...0xf7 */
233
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8...0xff */
236
+static const uint8_t addr_4a_reset_values[256] = {
237
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00...0x07 */
238
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08...0x0f */
239
+ 0xc0, 0x8c, 0xde, 0xde, 0x00, 0x00, 0x00, 0x00, /* 0x10...0x17 */
240
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18...0x1f */
241
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20...0x27 */
242
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28...0x2f */
243
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30...0x37 */
244
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38...0x3f */
245
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40...0x47 */
246
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48...0x4f */
247
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50...0x57 */
248
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58...0x5f */
249
+ 0x00, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x55, 0x07, /* 0x60...0x67 */
250
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68...0x6f */
251
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70...0x77 */
252
+ 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78...0x7f */
253
+ 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, /* 0x80...0x87 */
254
+ 0x00, 0x68, 0x9b, 0x86, 0x48, 0x2a, 0x07, 0x28, /* 0x88...0x8f */
255
+ 0x09, 0x69, 0x90, 0x00, 0x2a, 0x00, 0x02, 0x00, /* 0x90...0x97 */
256
+ 0x10, 0xcd, 0x02, 0x68, 0x03, 0x00, 0x00, 0x00, /* 0x98...0x9f */
257
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0...0xa7 */
258
+ 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, /* 0xa8...0xaf */
259
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0...0xb7 */
260
+ 0x00, 0x00, 0x00, 0xff, 0x0f, 0x00, 0x00, 0xff, /* 0xb8...0xbf */
261
+ 0x0f, 0x00, 0x00, 0xbf, 0x00, 0x00, 0x01, 0x00, /* 0xc0...0xc7 */
262
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8...0xcf */
263
+ 0x00, 0x00, 0x03, 0x00, 0x00, 0xe0, 0x00, 0x00, /* 0xd0...0xd7 */
264
+ 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8...0xdf */
265
+ 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x0f, 0x00, /* 0xe0...0xe7 */
266
+ 0x55, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8...0xef */
267
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0...0xf7 */
268
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* 0xf8...0xff */
271
+static const uint8_t addr_4b_reset_values[256] = {
272
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00...0x07 */
273
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08...0x0f */
274
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10...0x17 */
275
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, /* 0x18...0x1f */
276
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, /* 0x20...0x27 */
277
+ 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x01, 0x00, /* 0x28...0x2f */
278
+ 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0xbf, 0xbf, /* 0x30...0x37 */
279
+ 0xbf, 0xab, 0x00, 0x08, 0x3f, 0x15, 0x40, 0x0e, /* 0x38...0x3f */
280
+ 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40...0x47 */
281
+ 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, /* 0x48...0x4f */
282
+ 0x00, 0x02, 0x00, 0x04, 0x0d, 0x00, 0x00, 0x00, /* 0x50...0x57 */
283
+ 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58...0x5f */
284
+ 0x00, 0x00, 0x2f, 0x18, 0x0f, 0x08, 0x0f, 0x08, /* 0x60...0x67 */
285
+ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68...0x6f */
286
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x02, 0x80, 0x03, /* 0x70...0x77 */
287
+ 0x08, 0x09, 0x00, 0x00, 0x08, 0x03, 0x80, 0x03, /* 0x78...0x7f */
288
+ 0x08, 0x02, 0x00, 0x00, 0x08, 0x00, 0x80, 0x03, /* 0x80...0x87 */
289
+ 0x08, 0x08, 0x20, 0x00, 0x00, 0x02, 0x80, 0x04, /* 0x88...0x8f */
290
+ 0x08, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, /* 0x90...0x97 */
291
+ 0x08, 0x02, 0xe0, 0x01, 0x08, 0x00, 0xe0, 0x00, /* 0x98...0x9f */
292
+ 0x08, 0x01, 0xe0, 0x01, 0x08, 0x04, 0xe0, 0x03, /* 0xa0...0xa7 */
293
+ 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8...0xaf */
294
+ 0x20, 0x04, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0...0xb7 */
295
+ 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, /* 0xb8...0xbf */
296
+ 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, /* 0xc0...0xc7 */
297
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, /* 0xc8...0xcf */
298
+ 0x00, 0x08, 0xe0, 0x00, 0x08, 0x00, 0x00, 0x00, /* 0xd0...0xd7 */
299
+ 0x14, 0x08, 0xe0, 0x02, 0x08, 0xe0, 0x00, 0x08, /* 0xd8...0xdf */
300
+ 0xe0, 0x05, 0x08, 0xe0, 0x06, 0x08, 0xe0, 0x00, /* 0xe0...0xe7 */
301
+ 0x08, 0xe0, 0x00, 0x08, 0xe0, 0x06, 0x06, 0xe0, /* 0xe8...0xef */
302
+ 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0...0xf7 */
303
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* 0xf8...0xff */
306
+static void twl4030_interrupt_update(TWL4030State *s)
309
+ /* TODO: USB, BCI and GPIO interrupts */
312
+ if (((s->i2c[2]->reg_data[0xda] & 0x10) && /* SIR_EN */
313
+ s->i2c[2]->reg_data[0xe7]) || /* KEYP_SIR */
314
+ (s->i2c[2]->reg_data[0xe3] & /* KEYP_ISR1 */
315
+ ~s->i2c[2]->reg_data[0xe4])) /* KEYP_IMR1 */
316
+ x |= 0x02; /* PIH_ISR1 */
318
+ if (s->i2c[2]->reg_data[0x65] || /* MADC_SIR */
319
+ (s->i2c[2]->reg_data[0x61] & /* MADC_ISR1 */
320
+ ~s->i2c[2]->reg_data[0x62])) /* MADC_IMR1 */
321
+ x |= 0x08; /* PIH_ISR3 */
323
+ if ((s->i2c[3]->reg_data[0x2e] & /* PWR_ISR1 */
324
+ ~s->i2c[3]->reg_data[0x2f])) /* PWR_IMR1 */
325
+ x |= 0x20; /* PIH_ISR5 */
327
+ s->i2c[1]->reg_data[0x81] = x; /* PIH_ISR_P1 */
328
+ qemu_set_irq(s->irq1, x);
332
+ if (((s->i2c[2]->reg_data[0xda] & 0x10) && /* SIR_EN */
333
+ s->i2c[2]->reg_data[0xe7]) || /* KEYP_SIR */
334
+ (s->i2c[2]->reg_data[0xe5] & /* KEYP_ISR2 */
335
+ ~s->i2c[2]->reg_data[0xe6])) /* KEYP_IMR2 */
336
+ x |= 0x02; /* PIH_ISR1 */
338
+ if (s->i2c[2]->reg_data[0x65] || /* MADC_SIR */
339
+ (s->i2c[2]->reg_data[0x63] & /* MADC_ISR2 */
340
+ ~s->i2c[2]->reg_data[0x64])) /* MADC_IMR2 */
341
+ x |= 0x08; /* PIH_ISR3 */
343
+ if ((s->i2c[3]->reg_data[0x30] & /* PWR_ISR2 */
344
+ ~s->i2c[3]->reg_data[0x31])) /* PWR_IMR2 */
345
+ x |= 0x20; /* PIH_ISR5 */
347
+ s->i2c[1]->reg_data[0x82] = x; /* PIH_ISR_P2 */
348
+ qemu_set_irq(s->irq2, x);
352
+static uint8_t twl4030_48_read(TWL4030NodeState *s, uint8_t addr)
354
+ TRACE("addr=0x%02x", addr);
356
+ case 0x00: /* VENDOR_ID_LO */
357
+ case 0x01: /* VENDOR_ID_HI */
358
+ case 0x02: /* PRODUCT_ID_LO */
359
+ case 0x03: /* PRODUCT_ID_HI */
360
+ return s->reg_data[addr];
361
+ case 0x04: /* FUNC_CTRL */
362
+ case 0x05: /* FUNC_CRTL_SET */
363
+ case 0x06: /* FUNC_CRTL_CLR */
364
+ return s->reg_data[0x04];
365
+ case 0x07: /* IFC_CTRL */
366
+ case 0x08: /* IFC_CRTL_SET */
367
+ case 0x09: /* IFC_CRTL_CLR */
368
+ return s->reg_data[0x07];
369
+ case 0x13: /* USB_INT_STS */
370
+ case 0x16: /* SCRATCH_REG */
371
+ return s->reg_data[addr];
372
+ case 0xac: /* POWER_CTRL */
373
+ case 0xad: /* POWER_SET */
374
+ case 0xae: /* POWER_CLR */
375
+ return s->reg_data[0xac];
376
+ case 0xbb: /* CARKIT_AND_CTRL */
377
+ case 0xbc: /* CARKIT_ANA_SET */
378
+ case 0xbd: /* CARKIT_ANA_CLR */
379
+ return s->reg_data[0xbb];
380
+ case 0xfd: /* PHY_PWR_CTRL */
381
+ case 0xfe: /* PHY_CLK_CTRL */
382
+ return s->reg_data[addr];
383
+ case 0xff: /* PHY_CLK_CTRL_STS */
384
+ if (s->reg_data[0xfd] & 1) /* PHY_PWR_CTRL */
386
+ if (s->reg_data[0xfe] & 1) /* REQ_PHY_DPLL_CLK */
388
+ return (s->reg_data[0x04] >> 6) & 1; /* SUSPENDM */
390
+ hw_error("%s: unknown register 0x%02x", __FUNCTION__, addr);
396
+static void twl4030_48_write(TWL4030NodeState *s, uint8_t addr, uint8_t value)
398
+ TRACE("addr=0x%02x, value=0x%02x", addr, value);
400
+ case 0x04: /* FUNC_CTRL */
401
+ s->reg_data[0x04] = value & 0x7f;
403
+ case 0x05: /* FUNC_CRTL_SET */
404
+ s->reg_data[0x04] = (s->reg_data[0x04] | value) & 0x7f;
406
+ case 0x06: /* FUNC_CTRL_CLEAR */
407
+ s->reg_data[0x04] = (s->reg_data[0x04] & ~value) & 0x7f;
409
+ case 0x07: /* IFC_CTRL */
410
+ s->reg_data[0x07] = value & 0x9e;
412
+ case 0x08: /* IFC_CRTL_SET */
413
+ s->reg_data[0x07] = (s->reg_data[0x07] | value) & 0x9e;
415
+ case 0x09: /* IFC_CRTL_CLEAR */
416
+ s->reg_data[0x07] = (s->reg_data[0x07] & ~value) & 0x9e;
418
+ case 0x16: /* SCRATCH_REG */
419
+ s->reg_data[0x16] = value;
421
+ case 0xa1: /* CARKIT_SM_CTRL */
422
+ s->reg_data[0xa1] = value & 0x3f;
424
+ case 0xa2: /* CARKIT_SM_CTRL_SET */
425
+ s->reg_data[0xa1] = (s->reg_data[0xa1] | value) & 0x3f;
427
+ case 0xa3: /* CARKIT_SM_CTRL_CLR */
428
+ s->reg_data[0xa1] = (s->reg_data[0xa1] & ~value) & 0x3f;
430
+ case 0xac: /* POWER_CTRL */
431
+ s->reg_data[0xac] = value & 0x20;
433
+ case 0xad: /* POWER_SET */
434
+ s->reg_data[0xac] = (s->reg_data[0xac] | value) & 0x20;
436
+ case 0xae: /* POWER_CLEAR */
437
+ s->reg_data[0xac] = (s->reg_data[0xac] & ~value) & 0x20;
439
+ case 0xbb: /* CARKIT_ANA_CTRL */
440
+ s->reg_data[0xbb] = value;
442
+ case 0xbc: /* CARKIT_ANA_CTRL_SET */
443
+ s->reg_data[0xbb] |= value;
445
+ case 0xbd: /* CARKIT_ANA_CTRL_CLR */
446
+ s->reg_data[0xbb] &= ~value;
448
+ case 0xfd: /* PHY_PWR_CTRL */
449
+ s->reg_data[addr] = value & 0x1;
451
+ case 0xfe: /* PHY_CLK_CTRL */
452
+ s->reg_data[addr] = value & 0x7;
455
+ hw_error("%s: unknown register 0x%02x", __FUNCTION__, addr);
460
+static uint8_t twl4030_49_read(TWL4030NodeState *s, uint8_t addr)
462
+ TRACE("addr=0x%02x", addr);
464
+ /* AUDIO_VOICE region */
465
+ case 0x01 ... 0x49:
466
+ return s->reg_data[addr];
468
+ case 0x4c ... 0x60:
469
+ return s->reg_data[addr];
471
+ case 0x81: /* PIH_ISR_P1 */
472
+ case 0x82: /* PIH_ISR_P2 */
473
+ case 0x83: /* PIH_SIR */
474
+ return s->reg_data[addr];
476
+ case 0x85 ... 0x90:
477
+ if (s->reg_data[0x97] != 0x49) {
481
+ case 0x91 ... 0x97:
482
+ return s->reg_data[addr];
484
+ case 0x98 ... 0xc5:
485
+ return s->reg_data[addr];
487
+ hw_error("%s: unknown register 0x%02x", __FUNCTION__, addr);
493
+static void twl4030_49_write(TWL4030NodeState *s, uint8_t addr, uint8_t value)
495
+ TRACE("addr=0x%02x, value=0x%02x", addr, value);
497
+ /* AUDIO_VOICE region */
498
+ case 0x01 ... 0x49:
499
+ s->reg_data[addr] = value;
502
+ case 0x4c ... 0x59:
503
+ s->reg_data[addr] = value;
505
+ case 0x5a ... 0x60:
506
+ /* read-only, ignore */
509
+ case 0x81: /* PIH_ISR_P1 */
510
+ case 0x82: /* PIH_ISR_P2 */
511
+ case 0x83: /* PIH_SIR */
512
+ s->reg_data[addr] = value;
513
+ twl4030_interrupt_update(s->twl4030);
516
+ case 0x85 ... 0x90:
517
+ /* read-only, ignore */
519
+ case 0x91 ... 0x97:
520
+ s->reg_data[addr] = value;
523
+ case 0x98 ... 0x9a:
524
+ /* read-only, ignore */
526
+ case 0x9b ... 0xae:
527
+ s->reg_data[addr] = value;
529
+ case 0xaf: /* GPIOPUPDCTR5 */
530
+ s->reg_data[addr] = value & 0x0f;
532
+ case 0xb0 ... 0xb5:
533
+ s->reg_data[addr] = value;
535
+ case 0xb6: /* GPIO_IMR3A */
536
+ s->reg_data[addr] = value & 0x03;
538
+ case 0xb7 ... 0xc4:
539
+ s->reg_data[addr] = value;
541
+ case 0xc5: /* GPIO_SIH_CTRL */
542
+ s->reg_data[addr] = value & 0x07;
545
+ hw_error("%s: unknown register 0x%02x", __FUNCTION__, addr);
550
+static uint8_t twl4030_4a_read(TWL4030NodeState *s, uint8_t addr)
552
+ static const uint8_t twl5031_aciid_data[] = {
553
+ 0x55, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
555
+ TRACE("addr=0x%02x", addr);
558
+ case 0x00 ... 0x13:
560
+ case 0x64 ... 0x67:
561
+ return s->reg_data[addr];
562
+ case 0x17 ... 0x36: /* RT conversion result */
563
+ if (s->twl4030->madc_cb) {
564
+ uint16_t x = s->twl4030->madc_cb(TWL4030_ADC_RT,
565
+ (addr - 0x17) >> 1);
566
+ return (addr & 1) ? (uint8_t)((x & 3) << 6)
567
+ : (uint8_t)((x >> 2) & 0xff);
569
+ return s->reg_data[addr];
570
+ case 0x37 ... 0x56: /* GP conversion result */
571
+ if (s->twl4030->madc_cb) {
572
+ uint16_t x = s->twl4030->madc_cb(TWL4030_ADC_GP,
573
+ (addr - 0x37) >> 1);
574
+ return (addr & 1) ? (uint8_t)((x & 3) << 6)
575
+ : (uint8_t)((x >> 2) & 0xff);
577
+ return s->reg_data[addr];
578
+ case 0x57 ... 0x60: /* BCI conversion result */
579
+ if (s->twl4030->madc_cb) {
580
+ uint16_t x = s->twl4030->madc_cb(TWL4030_ADC_BCI,
581
+ (addr - 0x57) >> 1);
582
+ return (addr & 1) ? (uint8_t)((x & 3) << 6)
583
+ : (uint8_t)((x >> 2) & 0xff);
585
+ return s->reg_data[addr];
586
+ case 0x61: /* MADC_ISR1 */
587
+ case 0x63: /* MADC_ISR2 */
589
+ uint8_t data = s->reg_data[addr];
590
+ if (s->reg_data[0x67] & 0x04) { /* COR */
591
+ s->reg_data[addr] = 0x00;
592
+ twl4030_interrupt_update(s->twl4030);
596
+ /* MAIN_CHARGE(TWL4030) / ACCESSORY(TWL5031) region */
597
+ case 0x74 ... 0xa9:
598
+ if (s->twl4030->twl5031) {
600
+ case 0x74: /* ACIID */
601
+ if (s->twl4030->twl5031_aciid >=
602
+ sizeof(twl5031_aciid_data)) {
603
+ s->twl4030->twl5031_aciid = 0;
605
+ return twl5031_aciid_data[s->twl4030->twl5031_aciid++];
606
+ case 0x79: /* ACIIMR_LSB */
607
+ case 0x7a: /* ACIIMR_MSB */
608
+ case 0x7b: /* ACIIDR_LSB */
609
+ case 0x7c: /* ACIIDR_MSB */
610
+ case 0x80: /* AV_CTRL */
611
+ case 0x82: /* BCIA_CTRL */
612
+ case 0x83: /* ACCISR1 */
613
+ return s->reg_data[addr];
615
+ hw_error("%s: unknown twl5031 register 0x%02x",
616
+ __FUNCTION__, addr);
620
+ return s->reg_data[addr];
621
+ /* PRECHARGE region */
622
+ case 0xaa ... 0xb8:
623
+ return s->reg_data[addr];
624
+ /* Interrupt region */
625
+ case 0xb9 ... 0xc6:
626
+ return s->reg_data[addr];
627
+ /* KEYPAD region */
628
+ case 0xd2 ... 0xe2:
630
+ case 0xe6 ... 0xe9:
631
+ return s->reg_data[addr];
632
+ case 0xe3: /* KEYP_ISR1 */
633
+ case 0xe5: /* KEYP_ISR2 */
635
+ uint8_t data = s->reg_data[addr];
636
+ if (s->reg_data[0xe9] & 0x04) { /* COR */
637
+ s->reg_data[addr] = 0x00;
638
+ twl4030_interrupt_update(s->twl4030);
643
+ case 0xee: /* LEDEN */
644
+ return s->reg_data[addr];
646
+ case 0xef: /* PWMAON */
647
+ case 0xf0: /* PWMAOFF */
648
+ return s->reg_data[addr];
650
+ case 0xf1: /* PWMBON */
651
+ case 0xf2: /* PWMBOFF */
652
+ return s->reg_data[addr];
654
+ case 0xf8: /* PWM0ON */
655
+ case 0xf9: /* PWM0OFF */
656
+ return s->reg_data[addr];
658
+ case 0xfb: /* PWM1ON */
659
+ case 0xfc: /* PWM1OFF */
660
+ return s->reg_data[addr];
662
+ hw_error("%s: unknown register 0x%02x", __FUNCTION__, addr);
668
+static void twl4030_4a_write(TWL4030NodeState *s, uint8_t addr, uint8_t value)
670
+ TRACE("addr=0x%02x, value=0x%02x", addr, value);
674
+ case 0x00: /* CTRL1 */
675
+ case 0x01: /* CTRL2 */
676
+ s->reg_data[addr] = value;
678
+ case 0x06: /* SW1SELECT_LSB */
679
+ case 0x07: /* SW1SELECT_MSB */
680
+ case 0x08: /* SW1AVERAGE_LSB */
681
+ case 0x09: /* SW1AVERAGE_MSB */
682
+ case 0x0a: /* SW2SELECT_LSB */
683
+ case 0x0b: /* SW2SELECT_MSB */
684
+ case 0x0c: /* SW2AVERAGE_LSB */
685
+ case 0x0d: /* SW2AVERAGE_MSB */
686
+ s->reg_data[addr] = value;
688
+ case 0x12: /* CTRL_SW1 */
689
+ case 0x13: /* CTRL_SW2 */
690
+ /* always set all conversions ready, not busy */
691
+ s->reg_data[addr] = 0x3e;
692
+ if (value & 0x20) { /* SW1/SW2 */
693
+ s->reg_data[0x61] |= 2 << (addr - 0x12); /* SW1_ISR/SW2_ISR */
694
+ s->reg_data[0x63] |= 2 << (addr - 0x12); /* SW1_ISR/SW2_ISR */
695
+ twl4030_interrupt_update(s->twl4030);
698
+ case 0x17 ... 0x60: /* conversion results */
699
+ /* read-only, ignore */
701
+ case 0x61: /* MADC_ISR1 */
702
+ case 0x63: /* MADC_ISR2 */
703
+ if (!(s->reg_data[0x67] & 0x04)) { /* COR */
704
+ s->reg_data[addr] &= ~(value & 0x0f);
705
+ twl4030_interrupt_update(s->twl4030);
708
+ case 0x62: /* MADC_IMR1 */
709
+ case 0x64: /* MADC_IMR2 */
710
+ case 0x65: /* MADC_SIR */
711
+ s->reg_data[addr] = value & 0x0f;
712
+ twl4030_interrupt_update(s->twl4030);
714
+ case 0x66: /* MADC_EDR */
715
+ s->reg_data[addr] = value;
717
+ case 0x67: /* MADC_SIH_CTRL */
718
+ s->reg_data[addr] = value & 0x07;
721
+ /* MAIN_CHARGE(TWL4030) / ACCESSORY(TWL5031) region */
723
+ case 0x74: /* BCIMDEN(TWL4030) / ACIID(TWL5031) */
724
+ if (s->twl4030->twl5031) {
725
+ s->twl4030->twl5031_aciid = 0;
730
+ case 0x75: /* BCIMDKEY(TWL4030) / ACICOMR_LSB(TWL5031) */
731
+ s->reg_data[addr] = value;
732
+ if (!s->twl4030->twl5031) {
734
+ case 0x25: s->reg_data[0x74] = 0x12; break;
735
+ case 0x26: s->reg_data[0x74] = 0x11; break;
736
+ case 0x27: s->reg_data[0x74] = 0x0a; break;
737
+ case 0x28: s->reg_data[0x74] = 0x06; break;
738
+ case 0x29: s->reg_data[0x74] = 0x05; break;
739
+ default: s->reg_data[0x74] = 0; break;
743
+ case 0x76 ... 0x84:
744
+ if (s->twl4030->twl5031) {
746
+ case 0x79: /* ACIIMR_LSB */
747
+ s->reg_data[addr] = value;
748
+ twl4030_interrupt_update(s->twl4030);
750
+ case 0x7a: /* ACIIMR_MSB */
751
+ s->reg_data[addr] = value & 0x01;
752
+ twl4030_interrupt_update(s->twl4030);
754
+ case 0x7b: /* ACIIDR_LSB */
755
+ s->reg_data[addr] &= ~value;
756
+ twl4030_interrupt_update(s->twl4030);
758
+ case 0x7c: /* ACIIDR_MSB */
759
+ s->reg_data[addr] &= ~(value & 0x01);
760
+ twl4030_interrupt_update(s->twl4030);
762
+ case 0x7f: /* ECI_DBI_CTRL */
763
+ s->reg_data[addr] = value;
764
+ twl4030_interrupt_update(s->twl4030);
766
+ case 0x80: /* ACI_AV_CTRL */
767
+ s->reg_data[addr] = (s->reg_data[addr] & 0x18) |
769
+ twl4030_interrupt_update(s->twl4030);
771
+ case 0x82: /* BCIA_CTRL */
772
+ s->reg_data[addr] = value & 0x07;
774
+ case 0x83: /* ACCISR1 */
775
+ s->reg_data[addr] &= ~(value & 0x03);
776
+ twl4030_interrupt_update(s->twl4030);
778
+ case 0x84: /* ACCIMR1 */
779
+ s->reg_data[addr] = value & 0x03;
780
+ twl4030_interrupt_update(s->twl4030);
783
+ hw_error("%s: unknown twl5031 register 0x%02x",
784
+ __FUNCTION__, addr);
788
+ /* read-only registers */
791
+ case 0x97: /* BCICTL1 */
792
+ if (!s->twl4030->twl5031) {
793
+ s->reg_data[addr] = value;
795
+ hw_error("%s: unknown twl5031 register 0x%02x",
796
+ __FUNCTION__, addr);
800
+ /* PRECHARGE region */
802
+ case 0xaa ... 0xb8: /* FIXME: unknown registers */
803
+ s->reg_data[addr] = value;
806
+ /* Interrupt region */
808
+ case 0xb9: /* BCIISR1A */
809
+ s->reg_data[addr] &= ~value;
811
+ case 0xba: /* BCIISR2A */
812
+ s->reg_data[addr] &= ~(value & 0x0f);
814
+ case 0xbb: /* BCIIMR1A */
815
+ s->reg_data[addr] = value;
817
+ case 0xbc: /* BCIIMR2A */
818
+ s->reg_data[addr] = value & 0x0f;
820
+ case 0xc6: /* BCISIHCTRL */
821
+ s->reg_data[addr] = value & 0x07;
824
+ /* KEYPAD region */
826
+ case 0xd2: /* KEYP_CTRL_REG */
827
+ s->reg_data[addr] = value & 0x7f;
829
+ case 0xd3: /* KEYP_DEB_REG */
830
+ s->reg_data[addr] = value & 0x3f;
832
+ case 0xd5: /* LK_PTV_REG */
833
+ s->reg_data[addr] = value & 0xef;
835
+ case 0xda: /* KEYP_SMS */
836
+ s->reg_data[addr] = (s->reg_data[addr] & ~0x30) | (value & 0x30);
837
+ twl4030_interrupt_update(s->twl4030);
839
+ case 0xe3: /* KEYP_ISR1 */
840
+ case 0xe5: /* KEYP_ISR2 */
841
+ if (!(s->reg_data[0xe9] & 0x04)) { /* COR */
842
+ s->reg_data[addr] &= ~value;
843
+ twl4030_interrupt_update(s->twl4030);
846
+ case 0xe4: /* KEYP_IMR1 */
847
+ case 0xe6: /* KEYP_IMR2 */
848
+ case 0xe7: /* KEYP_SIR */
849
+ s->reg_data[addr] = value & 0x0f;
850
+ twl4030_interrupt_update(s->twl4030);
852
+ case 0xe9: /* KEYP_SIH_CTRL */
853
+ s->reg_data[addr] = value & 0x07;
855
+ case 0xd4: /* LONG_KEY_REG1 */
856
+ case 0xd6: /* TIME_OUT_REG1 */
857
+ case 0xd7: /* TIME_OUT_REG2 */
858
+ case 0xd8: /* KBC_REG */
859
+ case 0xe8: /* KEYP_EDR */
860
+ s->reg_data[addr] = value;
862
+ case 0xd9: /* KBR_REG */
863
+ case 0xdb ... 0xe2: /* FULL_CODE_xx_yy */
864
+ /* read-only, ignore */
869
+ case 0xee: /* LEDEN */
870
+ s->reg_data[addr] = value;
871
+ TRACE("LEDA power=%s/enable=%s, LEDB power=%s/enable=%s",
872
+ value & 0x10 ? "on" : "off", value & 0x01 ? "yes" : "no",
873
+ value & 0x20 ? "on" : "off", value & 0x02 ? "yes" : "no");
876
+ /* PWMA/B/0/1 regions */
878
+ case 0xef: /* PWMAON */
879
+ case 0xf1: /* PWMBON */
880
+ case 0xf8: /* PWM0ON */
881
+ case 0xfb: /* PWM1ON */
882
+ s->reg_data[addr] = value;
884
+ case 0xf0: /* PWMAOFF */
885
+ case 0xf2: /* PWMBOFF */
886
+ case 0xf9: /* PWM0OFF */
887
+ case 0xfc: /* PWM1OFF */
888
+ s->reg_data[addr] = value & 0x7f;
892
+ hw_error("%s: unknown register 0x%02x", __FUNCTION__, addr);
897
+static inline struct tm *twl4030_gettime(void)
899
+ time_t epoch_time = time(NULL);
900
+ return gmtime(&epoch_time);//localtime(&epoch_time);
903
+static uint8_t twl4030_4b_read(TWL4030NodeState *s, uint8_t addr)
906
+ TRACE("addr=0x%02x value=0x%02x", addr, s->reg_data[addr]);
908
+ /* SECURED_REG region */
909
+ case 0x00 ... 0x13:
910
+ return s->reg_data[addr];
911
+ /* BACKUP_REG region */
912
+ case 0x14 ... 0x1b:
913
+ return s->reg_data[addr];
915
+ case 0x1c: /* SECONDS_REG */
916
+ x = s->reg_data[addr];
918
+ struct tm *t = twl4030_gettime();
919
+ x = ((t->tm_sec / 10) << 4) | (t->tm_sec % 10);
921
+ s->reg_data[addr] = 0xff;
923
+ TRACE_RTC("SECONDS_REG returns 0x%02x", x);
925
+ case 0x1d: /* MINUTES_REG */
926
+ x = s->reg_data[addr];
928
+ struct tm *t = twl4030_gettime();
929
+ x = ((t->tm_min / 10) << 4) | (t->tm_min % 10);
931
+ s->reg_data[addr] = 0xff;
933
+ TRACE_RTC("MINUTES_REG returns 0x%02x", x);
935
+ case 0x1e: /* HOURS_REG */
936
+ x = s->reg_data[addr];
938
+ struct tm *t = twl4030_gettime();
939
+ if (s->reg_data[0x29] & 0x08) { /* MODE_12_24 */
940
+ int h12 = t->tm_hour;
943
+ x = ((h12 / 10) << 4) | (h12 % 10) | 0x80; /* PM_NAM */
945
+ x = ((h12 / 10) << 4) | (h12 % 10);
948
+ x = ((t->tm_hour / 10) << 4) | (t->tm_hour % 10);
951
+ s->reg_data[addr] = 0xff;
953
+ TRACE_RTC("HOURS_REG returns 0x%02x", x);
955
+ case 0x1f: /* DAYS_REG */
956
+ x = s->reg_data[addr];
958
+ struct tm *t = twl4030_gettime();
959
+ x = ((t->tm_mday / 10) << 4) | (t->tm_mday % 10);
961
+ s->reg_data[addr] = 0xff;
963
+ TRACE_RTC("DAYS_REG returns 0x%02x", x);
965
+ case 0x20: /* MONTHS_REG */
966
+ x = s->reg_data[addr];
968
+ struct tm *t = twl4030_gettime();
969
+ x = (((t->tm_mon + 1) / 10) << 4) | ((t->tm_mon + 1) % 10);
971
+ s->reg_data[addr] = 0xff;
973
+ TRACE_RTC("MONTHS_REG returns 0x%02x", x);
975
+ case 0x21: /* YEARS_REG */
976
+ x = s->reg_data[addr];
978
+ struct tm *t = twl4030_gettime();
979
+ x = (((t->tm_year % 100) / 10) << 4) | (t->tm_year % 10);
981
+ s->reg_data[addr] = 0xff;
983
+ TRACE_RTC("YEARS_REG returns 0x%02x", x);
985
+ case 0x22: /* WEEKS_REG */
986
+ x = s->reg_data[addr];
988
+ struct tm *t = twl4030_gettime();
991
+ s->reg_data[addr] = 0xff;
993
+ TRACE_RTC("WEEKS_REG returns 0x%02x", x);
995
+ case 0x23: /* ALARM_SECONDS_REG */
996
+ x = s->reg_data[addr];
997
+ TRACE_RTC("ALARM_SECONDS_REG returns 0x%02x", x);
999
+ case 0x24: /* ALARM_MINUTES_REG */
1000
+ x = s->reg_data[addr];
1001
+ TRACE_RTC("ALARM_MINUTES_REG returns 0x%02x", x);
1003
+ case 0x25: /* ALARM_HOURS_REG */
1004
+ x = s->reg_data[addr];
1005
+ TRACE_RTC("ALARM_HOURS_REG returns 0x%02x", x);
1007
+ case 0x26: /* ALARM_DAYS_REG */
1008
+ x = s->reg_data[addr];
1009
+ TRACE_RTC("ALARM_DAYS_REG returns 0x%02x", x);
1011
+ case 0x27: /* ALARM_MONTHS_REG */
1012
+ x = s->reg_data[addr];
1013
+ TRACE_RTC("ALARM_MONTHS_REG returns 0x%02x", x);
1015
+ case 0x28: /* ALARM_YEARS_REG */
1016
+ x = s->reg_data[addr];
1017
+ TRACE_RTC("ALARM_YEARS_REG returns 0x%02x", x);
1019
+ case 0x29: /* RTC_CTRL_REG */
1020
+ x = s->reg_data[addr];
1021
+ TRACE_RTC("RTC_CTRL_REG returns 0x%02x", x);
1023
+ case 0x2a: /* RTC_STATUS_REG */
1024
+ x = s->reg_data[addr];
1025
+ TRACE_RTC("RTC_STATUS_REG returns 0x%02x", x);
1027
+ case 0x2b: /* RTC_INTERRUPTS_REG */
1028
+ x = s->reg_data[addr];
1029
+ TRACE_RTC("RTC_INTERRUPTS_REG returns 0x%02x", x);
1031
+ case 0x2c: /* RTC_COMP_LSB_REG */
1032
+ x = s->reg_data[addr];
1033
+ TRACE_RTC("RTC_COMP_LSB_REG returns 0x%02x", x);
1035
+ case 0x2d: /* RTC_COMP_MSB_REG */
1036
+ x = s->reg_data[addr];
1037
+ TRACE_RTC("RTC_CTRL_REG returns 0x%02x", x);
1041
+ case 0x31 ... 0x35:
1042
+ return s->reg_data[addr];
1043
+ case 0x2e: /* PWR_ISR1 */
1044
+ case 0x30: /* PWR_ISR2 */
1046
+ uint8_t data = s->reg_data[addr];
1047
+ if (s->reg_data[0x35] & 0x04) { /* COR */
1048
+ s->reg_data[addr] = 0x00;
1049
+ twl4030_interrupt_update(s->twl4030);
1053
+ /* PM_MASTER region */
1054
+ case 0x36 ... 0x44:
1055
+ return s->reg_data[addr];
1056
+ case 0x45: /* STS_HW_CONDITIONS */
1057
+ /* FIXME: force USB always connected, no VBUS (host usb) */
1058
+ return (s->reg_data[addr] & ~0x80) | 0x04;
1059
+ case 0x46 ... 0x5a:
1060
+ return s->reg_data[addr];
1061
+ /* PM_RECEIVER region */
1062
+ case 0x5b ... 0xf1:
1063
+ return s->reg_data[addr];
1065
+ hw_error("%s: unknown register 0x%02x", __FUNCTION__, addr);
1071
+static void twl4030_setup_alarm(TWL4030NodeState *s)
1073
+ if (s->reg_data[0x2b] & 0x08) { /* IT_ALARM */
1075
+ .tm_sec = ((s->reg_data[0x23] >> 4) & 7) * 10
1076
+ + (s->reg_data[0x23] & 0x0f),
1077
+ .tm_min = ((s->reg_data[0x24] >> 4) & 7) * 10
1078
+ + (s->reg_data[0x24] & 0x0f),
1079
+ .tm_hour = ((s->reg_data[0x29] & 0x08)
1080
+ ? (s->reg_data[0x25] >> 7) * 12 : 0)
1081
+ + ((s->reg_data[0x25] >> 4) & 3) * 10
1082
+ + (s->reg_data[0x25] & 0x0f),
1083
+ .tm_mday = ((s->reg_data[0x26] >> 4) & 3) * 10
1084
+ + (s->reg_data[0x26] & 0x0f),
1085
+ .tm_mon = ((s->reg_data[0x27] >> 4) & 1) * 10
1086
+ + (s->reg_data[0x27] & 0x0f)
1088
+ .tm_year = (s->reg_data[0x28] >> 4) * 10
1089
+ + (s->reg_data[0x28] & 0x0f)
1093
+ TRACE_RTC("enable alarm on %02d/%02d/%04d at %02d:%02d:%02d (UTC)",
1094
+ a.tm_mday, a.tm_mon + 1, a.tm_year + 1900,
1095
+ a.tm_hour, a.tm_min, a.tm_sec);
1096
+ time_t at = mktime(&a); /* alarm time interpreted in local time */
1098
+ TRACE_RTC("unable to parse alarm calendar time");
1100
+ /* fix alarm time to utc */
1101
+ struct timezone tz;
1102
+ struct timeval tv;
1103
+ if (!gettimeofday(&tv, &tz)) {
1104
+ at -= tz.tz_minuteswest * 60;
1106
+ int64_t delta = (int64_t)difftime(at, time(NULL));
1108
+ TRACE_RTC("alarm is in the past");
1110
+ TRACE_RTC("new alarm interrupt in %" PRId64 " seconds", delta);
1111
+ qemu_mod_timer(s->twl4030->alarm_timer,
1112
+ qemu_get_clock_ns(vm_clock)
1113
+ + get_ticks_per_sec() * delta);
1117
+ qemu_del_timer(s->twl4030->alarm_timer);
1121
+static void twl4030_setup_periodic(TWL4030NodeState *s)
1123
+ if (s->reg_data[0x2b] & 0x04) { /* IT_TIMER */
1125
+ switch (s->reg_data[0x2b] & 3) {
1126
+ case 0: t = 1; break;
1127
+ case 1: t = 60; break;
1128
+ case 2: t = 60 * 60; break;
1129
+ case 3: t = 24 * 60 * 60; break;
1131
+ TRACE_RTC("new periodic interrupt in %u seconds", t);
1132
+ qemu_mod_timer(s->twl4030->periodic_timer,
1133
+ qemu_get_clock_ns(vm_clock) + get_ticks_per_sec() * t);
1135
+ qemu_del_timer(s->twl4030->periodic_timer);
1139
+static void twl4030_alarm(void *opaque)
1141
+ TWL4030State *s = opaque;
1142
+ s->i2c[3]->reg_data[0x2a] |= 0x40; /* RTC_STATUS_REG |= ALARM */
1143
+ if (s->i2c[3]->reg_data[0x33] & 0xc0) { /* RTC_IT_RISING|RTC_IT_FALLING */
1144
+ TRACE_RTC("triggering RTC alarm interrupt");
1145
+ s->i2c[3]->reg_data[0x2e] |= 0x08; /* PWR_ISR1 |= RTC_IT */
1146
+ s->i2c[3]->reg_data[0x30] |= 0x08; /* PWR_ISR2 |= RTC_IT */
1147
+ twl4030_interrupt_update(s);
1149
+ qemu_del_timer(s->alarm_timer);
1152
+static void twl4030_periodic(void *opaque)
1154
+ TWL4030State *s = opaque;
1155
+ uint8_t b = 0x04 << (s->i2c[3]->reg_data[0x2b] & 3);
1156
+ s->i2c[3]->reg_data[0x2a] |= b; /* TODO: when are these cleared? */
1157
+ if (s->i2c[3]->reg_data[0x33] & 0xc0) { /* RTC_IT_RISING|RTC_IT_FALLING */
1158
+ TRACE_RTC("triggering RTC periodic interrupt");
1159
+ s->i2c[3]->reg_data[0x2e] |= 0x08; /* PWR_ISR1 |= RTC_IT */
1160
+ s->i2c[3]->reg_data[0x30] |= 0x08; /* PWR_ISR2 |= RTC_IT */
1161
+ twl4030_interrupt_update(s);
1163
+ twl4030_setup_periodic(s->i2c[3]);
1166
+static void twl4030_4b_write(TWL4030NodeState *s, uint8_t addr, uint8_t value)
1168
+ uint8_t seq_addr, seq_sub;
1170
+ TRACE("addr=0x%02x, value=0x%02x", addr, value);
1172
+ case 0x1c: /* SECONDS_REG */
1173
+ TRACE_RTC("SECONDS_REG = 0x%02x", value);
1174
+ s->reg_data[addr] = value & 0x7f;
1176
+ case 0x1d: /* MINUTES_REG */
1177
+ TRACE_RTC("MINUTES_REG = 0x%02x", value);
1178
+ s->reg_data[addr] = value & 0x7f;
1180
+ case 0x1e: /* HOURS_REG */
1181
+ TRACE_RTC("HOURS_REG = 0x%02x", value);
1182
+ s->reg_data[addr] = value & 0xbf;
1184
+ case 0x1f: /* DAYS_REG */
1185
+ TRACE_RTC("DAYS_REG = 0x%02x", value);
1186
+ s->reg_data[addr] = value & 0x3f;
1188
+ case 0x20: /* MONTHS_REG */
1189
+ TRACE_RTC("MONTHS_REG = 0x%02x", value);
1190
+ s->reg_data[addr] = value & 0x1f;
1192
+ case 0x21: /* YEARS_REG */
1193
+ TRACE_RTC("YEARS_REG = 0x%02x", value);
1194
+ s->reg_data[addr] = value;
1196
+ case 0x22: /* WEEKS_REG */
1197
+ TRACE_RTC("WEEKS_REG = 0x%02x", value);
1198
+ s->reg_data[addr] = value & 0x07;
1200
+ case 0x23: /* ALARM_SECONDS_REG */
1201
+ TRACE_RTC("ALARM_SECONDS_REG = 0x%02x", value);
1202
+ s->reg_data[addr] = value & 0x7f;
1203
+ twl4030_setup_alarm(s);
1205
+ case 0x24: /* ALARM_MINUTES_REG */
1206
+ TRACE_RTC("ALARM_MINUTES_REG = 0x%02x", value);
1207
+ s->reg_data[addr] = value & 0x7f;
1208
+ twl4030_setup_alarm(s);
1210
+ case 0x25: /* ALARM_HOURS_REG */
1211
+ TRACE_RTC("ALARM_HOURS_REG = 0x%02x", value);
1212
+ s->reg_data[addr] = value & 0xbf;
1213
+ twl4030_setup_alarm(s);
1215
+ case 0x26: /* ALARM_DAYS_REG */
1216
+ TRACE_RTC("ALARM_DAYS_REG = 0x%02x", value);
1217
+ s->reg_data[addr] = value & 0x3f;
1218
+ twl4030_setup_alarm(s);
1220
+ case 0x27: /* ALARM_MONTHS_REG */
1221
+ TRACE_RTC("ALARM_MONTHS_REG = 0x%02x", value);
1222
+ s->reg_data[addr] = value & 0x1f;
1223
+ twl4030_setup_alarm(s);
1225
+ case 0x28: /* ALARM_YEARS_REG */
1226
+ TRACE_RTC("ALARM_YEARS_REG = 0x%02x", value);
1227
+ s->reg_data[addr] = value;
1228
+ twl4030_setup_alarm(s);
1230
+ case 0x29: /* RTC_CTRL_REG */
1231
+ TRACE_RTC("RTC_CTRL_REG = 0x%02x", value);
1232
+ s->reg_data[addr] = value & 0x3f;
1233
+ s->reg_data[0x2a] = (s->reg_data[0x2a] & ~0x02) |
1234
+ ((value & 0x01) << 1);
1235
+ if (value & 0x40) { /* GET_TIME */
1236
+ struct tm *t = twl4030_gettime();
1237
+ s->reg_data[0x1c] = ((t->tm_sec / 10) << 4) | (t->tm_sec % 10);
1238
+ s->reg_data[0x1d] = ((t->tm_min / 10) << 4) | (t->tm_min % 10);
1239
+ if (value & 0x08) { /* MODE_12_24 */
1240
+ int h12 = t->tm_hour;
1241
+ /* TODO: should we report hours 0-11 or 1-12? */
1244
+ s->reg_data[0x1e] = ((h12 / 10) << 4) | (h12 % 10) |
1245
+ 0x80; /* PM_NAM */
1247
+ s->reg_data[0x1e] = ((h12 / 10) << 4) | (h12 % 10);
1250
+ s->reg_data[0x1e] = ((t->tm_hour / 10) << 4) |
1251
+ (t->tm_hour % 10);
1253
+ s->reg_data[0x1f] = ((t->tm_mday / 10) << 4) |
1254
+ (t->tm_mday % 10);
1255
+ s->reg_data[0x20] = (((t->tm_mon + 1) / 10) << 4) |
1256
+ ((t->tm_mon + 1) % 10);
1257
+ s->reg_data[0x21] = (((t->tm_year % 100) / 10) << 4) |
1258
+ (t->tm_year % 10);
1259
+ s->reg_data[0x22] = t->tm_wday;
1261
+ /* TODO: support bits 1, 2, 4 and 5 */
1263
+ case 0x2a: /* RTC_STATUS_REG */
1264
+ TRACE_RTC("RTC_STATUS_REG = 0x%02x", value);
1265
+ s->reg_data[addr] &= ~(value & 0xc0);
1267
+ case 0x2b: /* RTC_INTERRUPTS_REG */
1268
+ TRACE_RTC("RTC_INTERRUPTS_REG = 0x%02x", value);
1270
+ uint8_t change = s->reg_data[addr] ^ value;
1271
+ s->reg_data[addr] = value & 0x0f;
1272
+ if (change & 0x08) { /* IT_ALARM */
1273
+ twl4030_setup_alarm(s);
1275
+ if (change & 0x04) { /* IT_TIMER */
1276
+ twl4030_setup_periodic(s);
1280
+ case 0x2c: /* RTC_COMP_LSB_REG */
1281
+ case 0x2d: /* RTC_COMP_MSB_REG */
1282
+ TRACE_RTC("RTC_COMP_%s_REG = 0x%02x",
1283
+ (addr == 0x2c) ? "LSB" : "MSB", value);
1284
+ s->reg_data[addr] = value;
1286
+ case 0x2e: /* PWR_ISR1 */
1287
+ case 0x30: /* PWR_ISR2 */
1288
+ if (!(s->reg_data[0x35] & 0x04)) { /* COR */
1289
+ s->reg_data[addr] &= ~value;
1290
+ twl4030_interrupt_update(s->twl4030);
1293
+ case 0x2f: /* PWR_IMR1 */
1294
+ case 0x31: /* PWR_IMR2 */
1295
+ s->reg_data[addr] = value;
1296
+ twl4030_interrupt_update(s->twl4030);
1298
+ case 0x33: /* PWR_EDR1 */
1299
+ case 0x34: /* PWR_EDR2 */
1300
+ s->reg_data[addr] = value;
1302
+ case 0x35: /* PWR_SIH_CTRL */
1303
+ s->reg_data[addr] = value & 0x07;
1305
+ case 0x36: /* CFG_P1_TRANSITION */
1306
+ case 0x37: /* CFG_P2_TRANSITION */
1307
+ case 0x38: /* CFG_P3_TRANSITION */
1308
+ if (s->twl4030->key_cfg)
1309
+ s->reg_data[addr] = (s->reg_data[addr] & 0x40) | (value & 0xbf);
1311
+ case 0x39: /* CFG_P123_TRANSITION */
1312
+ if (s->twl4030->key_cfg)
1313
+ s->reg_data[addr] = value;
1315
+ case 0x3a: /* STS_BOOT */
1316
+ s->reg_data[addr] = value;
1318
+ case 0x3b: /* CFG_BOOT */
1319
+ if (s->twl4030->key_cfg)
1320
+ s->reg_data[addr] = (s->reg_data[addr] & 0x70) | (value & 0x8f);
1322
+ case 0x3c: /* SHUNDAN */
1323
+ s->reg_data[addr] = value & 0x3f;
1325
+ case 0x3d: /* BOOT_BCI */
1326
+ s->reg_data[addr] = (s->reg_data[addr] & 0x20) | (value & 0x17);
1328
+ case 0x3e: /* CFG_PWRANA1 */
1329
+ if (s->twl4030->key_tst)
1330
+ s->reg_data[addr] = value & 0x7f;
1332
+ case 0x3f: /* CFG_PWRANA2 */
1333
+ if (s->twl4030->key_tst)
1334
+ s->reg_data[addr] = value;
1336
+ case 0x44: /* PROTECT_KEY */
1337
+ s->twl4030->key_cfg = 0;
1338
+ s->twl4030->key_tst = 0;
1341
+ if (s->reg_data[addr] == 0xC0)
1342
+ s->twl4030->key_cfg = 1;
1345
+ if (s->reg_data[addr] == 0x0E)
1346
+ s->twl4030->key_tst = 1;
1349
+ if (s->reg_data[addr] == 0xCE) {
1350
+ s->twl4030->key_cfg = 1;
1351
+ s->twl4030->key_tst = 1;
1357
+ s->reg_data[addr] = value;
1359
+ case 0x46: /* P1_SW_EVENTS */
1360
+ case 0x47: /* P2_SW_EVENTS */
1361
+ case 0x48: /* P3_SW_EVENTS */
1362
+ s->reg_data[addr] = value & 0x78;
1363
+ if (value & 0x01) { /* DEVOFF */
1364
+ TRACE("device power off sequence requested");
1365
+ qemu_system_shutdown_request();
1368
+ case 0x4a: /* PB_CFG */
1369
+ s->reg_data[addr] = value & 0xf;
1371
+ case 0x4b: /* PB_MSB */
1372
+ case 0x4c: /* PB_LSB */
1373
+ s->reg_data[addr] = value;
1375
+ case 0x52: /* SEQ_ADD_W2P */
1376
+ case 0x53: /* SEQ_ADD_P2A */
1377
+ case 0x54: /* SEQ_ADD_A2W */
1378
+ case 0x55: /* SEQ_ADD_A2S */
1379
+ case 0x56: /* SEQ_ADD_S2A12 */
1380
+ case 0x57: /* SEQ_ADD_S2A3 */
1381
+ case 0x58: /* SEQ_ADD_WARM */
1382
+ if (s->twl4030->key_cfg)
1383
+ s->reg_data[addr] = value & 0x3f;
1385
+ case 0x59: /* MEMORY_ADDRESS */
1386
+ if (s->twl4030->key_cfg)
1387
+ s->reg_data[addr] = value;
1389
+ case 0x5a: /* MEMORY_DATA */
1390
+ if (s->twl4030->key_cfg) {
1391
+ s->reg_data[addr] = value;
1392
+ seq_addr = s->reg_data[0x59];
1393
+ seq_sub = seq_addr & 3;
1395
+ if ((seq_addr >= 0x2b && seq_addr <= 0x3e) ||
1396
+ (seq_addr <= 0x0e && seq_sub == 3))
1397
+ s->twl4030->seq_mem[seq_addr][seq_sub] = value;
1399
+ /* TODO: check if autoincrement is write-protected as well */
1400
+ s->reg_data[0x59]++;
1402
+ case 0x5e: /* WATCHDOG_CFG */
1403
+ case 0x60: /* VIBRATOR_CFG */
1404
+ case 0x6d: /* BB_CFG */
1405
+ case 0x73: /* VAUX1_TYPE */
1406
+ case 0x77: /* VAUX2_TYPE */
1407
+ case 0x7b: /* VAUX3_TYPE */
1408
+ case 0x7f: /* VAUX4_TYPE */
1409
+ case 0x83: /* VMMC1_TYPE */
1410
+ case 0x87: /* VMMC2_TYPE */
1411
+ case 0x8b: /* VPLL1_TYPE */
1412
+ case 0x8f: /* VPLL2_TYPE */
1413
+ case 0x93: /* VSIM_TYPE */
1414
+ case 0x97: /* VDAC_TYPE */
1415
+ case 0x9b: /* VINTANA1_TYPE */
1416
+ case 0x9f: /* VINTANA2_TYPE */
1417
+ case 0xa3: /* VINTDIG_TYPE */
1418
+ case 0xa7: /* VIO_TYPE */
1419
+ case 0xaa: /* VIO_MISC_CFG */
1420
+ case 0xb1: /* VDD1_TYPE */
1421
+ case 0xb4: /* VDD1_MISC_CFG */
1422
+ case 0xbd: /* VDD1_STEP */
1423
+ case 0xbf: /* VDD2_TYPE */
1424
+ case 0xc2: /* VDD2_MISC_CFG */
1425
+ case 0xcb: /* VDD2_STEP */
1426
+ case 0xcd: /* VUSB1V5_TYPE */
1427
+ case 0xd0: /* VUSB1V8_TYPE */
1428
+ case 0xd3: /* VUSB3V1_TYPE */
1429
+ case 0xdb: /* REGEN_TYPE */
1430
+ case 0xde: /* NRESPWRON_TYPE */
1431
+ case 0xe1: /* CLKEN_TYPE */
1432
+ case 0xe4: /* SYSEN_TYPE */
1433
+ case 0xe7: /* HFCLKOUT_TYPE */
1434
+ case 0xea: /* 2KCLKOUT_TYPE */
1435
+ case 0xed: /* TRITON_RESET_TYPE */
1436
+ case 0xf0: /* MAINREF_TYPE */
1437
+ s->reg_data[addr] = value & 0x1f;
1439
+ case 0x5f: /* IT_CHECK_CFG */
1440
+ case 0xb9: /* VDD1_VSEL */
1441
+ case 0xbb: /* VDD1_VFLOOR */
1442
+ case 0xbc: /* VDD1_VROOF */
1443
+ case 0xc7: /* VDD2_VSEL */
1444
+ case 0xc9: /* VDD2_VFLOOR */
1445
+ case 0xca: /* VDD2_VROOF */
1446
+ s->reg_data[addr] = value & 0x7f;
1448
+ case 0x61: /* DC/DC_GLOBAL_CFG */
1449
+ case 0x68: /* MISC_CFG */
1450
+ s->reg_data[addr] = value;
1452
+ case 0x62: /* VDD1_TRIM1 */
1453
+ case 0x64: /* VDD2_TRIM1 */
1454
+ case 0x66: /* VIO_TRIM1 */
1455
+ case 0xac: /* VIO_TEST2 */
1456
+ case 0xb6: /* VDD1_TEST2 */
1457
+ case 0xc4: /* VDD2_TEST2 */
1458
+ if (s->twl4030->key_tst)
1459
+ s->reg_data[addr] = value & 0x7f;
1461
+ case 0x63: /* VDD1_TRIM2 */
1462
+ case 0x65: /* VDD2_TRIM2 */
1463
+ case 0x67: /* VIO_TRIM2 */
1464
+ if (s->twl4030->key_tst)
1465
+ s->reg_data[addr] = value & 0x3f;
1467
+ case 0x72: /* VAUX1_DEV_GRP */
1468
+ case 0x76: /* VAUX2_DEV_GRP */
1469
+ case 0x7a: /* VAUX3_DEV_GRP */
1470
+ case 0x7e: /* VAUX4_DEV_GRP */
1471
+ case 0x82: /* VMMC1_DEV_GRP */
1472
+ case 0x86: /* VMMC2_DEV_GRP */
1473
+ case 0x8a: /* VPLL1_DEV_GRP */
1474
+ case 0x8e: /* VPLL2_DEV_GRP */
1475
+ case 0x92: /* VSIM_DEV_GRP */
1476
+ case 0x96: /* VDAC_DEV_GRP */
1477
+ case 0x9a: /* VINTANA1_DEV_GRP */
1478
+ case 0x9e: /* VINTANA2_DEV_GRP */
1479
+ case 0xa2: /* VINTDIG_DEV_GRP */
1480
+ case 0xa6: /* VIO_DEV_GRP */
1481
+ case 0xb0: /* VDD1_DEV_GRP */
1482
+ case 0xbe: /* VDD2_DEV_GRP */
1483
+ case 0xcc: /* VUSB1V5_DEV_GRP */
1484
+ case 0xcf: /* VUSB1V8_DEV_GRP */
1485
+ case 0xd2: /* VUSB3V1_DEV_GRP */
1486
+ case 0xda: /* REGEN_DEV_GRP */
1487
+ case 0xdd: /* NRESPWRON_DEV_GRP */
1488
+ case 0xe0: /* CLKEN_DEV_GRP */
1489
+ case 0xe3: /* SYSEN_DEV_GRP */
1490
+ case 0xe6: /* HFCLKOUT_DEV_GRP */
1491
+ case 0xe9: /* 2KCLKOUT_DEV_GRP */
1492
+ case 0xec: /* TRITON_RESET_DEV_GRP */
1493
+ case 0xef: /* MAINREF_DEV_GRP */
1494
+ s->reg_data[addr] = (s->reg_data[addr] & 0x0f) | (value & 0xf0);
1496
+ case 0x75: /* VAUX1_DEDICATED */
1497
+ case 0x7d: /* VAUX3_DEDICATED */
1498
+ case 0x8d: /* VPLL1_DEDICATED */
1499
+ case 0x95: /* VSIM_DEDICATED */
1500
+ if (s->twl4030->key_tst)
1501
+ s->reg_data[addr] = value & 0x77;
1503
+ s->reg_data[addr] = (s->reg_data[addr] & 0x70) | (value & 0x07);
1505
+ case 0x79: /* VAUX2_DEDICATED */
1506
+ case 0x81: /* VAUX4_DEDICATED */
1507
+ case 0x91: /* VPLL2_DEDICATED */
1508
+ case 0xa5: /* VINTDIG_DEDICATED */
1509
+ if (s->twl4030->key_tst)
1510
+ s->reg_data[addr] = value & 0x7f;
1512
+ s->reg_data[addr] = (s->reg_data[addr] & 0x70) | (value & 0x0f);
1514
+ case 0x85: /* VMMC1_DEDICATED */
1515
+ case 0x99: /* VDAC_DEDICATED */
1516
+ if (s->twl4030->key_tst)
1517
+ s->reg_data[addr] = value & 0x73;
1519
+ s->reg_data[addr] = (s->reg_data[addr] & 0x70) | (value & 0x03);
1521
+ case 0x89: /* VMMC2_DEDICATED */
1522
+ if (s->twl4030->key_tst)
1523
+ s->reg_data[addr] = value & 0x7f;
1525
+ s->reg_data[addr] = (s->reg_data[addr] & 0x70) | (value & 0x0f);
1527
+ case 0x9d: /* VINTANA1_DEDICATED */
1528
+ if (s->twl4030->key_tst)
1529
+ s->reg_data[addr] = value & 0x70;
1531
+ case 0xa1: /* VINTANA2_DEDICATED */
1532
+ if (s->twl4030->key_tst)
1533
+ s->reg_data[addr] = value & 0x71;
1535
+ s->reg_data[addr] = (s->reg_data[addr] & 0x70) | (value & 0x01);
1537
+ case 0x74: /* VAUX1_REMAP */
1538
+ case 0x78: /* VAUX2_REMAP */
1539
+ case 0x7c: /* VAUX3_REMAP */
1540
+ case 0x80: /* VAUX4_REMAP */
1541
+ case 0x84: /* VMMC1_REMAP */
1542
+ case 0x88: /* VMMC2_REMAP */
1543
+ case 0x8c: /* VPLL1_REMAP */
1544
+ case 0x90: /* VPLL2_REMAP */
1545
+ case 0x94: /* VSIM_REMAP */
1546
+ case 0x98: /* VDAC_REMAP */
1547
+ case 0x9c: /* VINTANA1_REMAP */
1548
+ case 0xa0: /* VINTANA2_REMAP */
1549
+ case 0xa4: /* VINTDIG_REMAP */
1550
+ case 0xa8: /* VIO_REMAP */
1551
+ case 0xb2: /* VDD1_REMAP */
1552
+ case 0xc0: /* VDD2_REMAP */
1553
+ case 0xce: /* VUSB1V5_REMAP */
1554
+ case 0xd1: /* VUSB1V8_REMAP */
1555
+ case 0xd4: /* VUSB3V1_REMAP */
1556
+ case 0xdc: /* REGEN_REMAP */
1557
+ case 0xdf: /* NRESPWRON_REMAP */
1558
+ case 0xe2: /* CLKEN_REMAP */
1559
+ case 0xe5: /* SYSEN_REMAP */
1560
+ case 0xe8: /* HFCLKOUT_REMAP */
1561
+ case 0xeb: /* 2KCLKOUT_REMAP */
1562
+ case 0xee: /* TRITON_RESET_REMAP */
1563
+ case 0xf1: /* MAINREF_REMAP */
1564
+ s->reg_data[addr] = value;
1566
+ case 0xa9: /* VIO_CFG */
1567
+ case 0xb3: /* VDD1_CFG */
1568
+ case 0xc1: /* VDD2_CFG */
1569
+ s->reg_data[addr] = value & 0x03;
1571
+ case 0xab: /* VIO_TEST1 */
1572
+ case 0xb5: /* VDD1_TEST1 */
1573
+ case 0xc3: /* VDD2_TEST1 */
1574
+ if (s->twl4030->key_tst)
1575
+ s->reg_data[addr] = value;
1577
+ case 0xad: /* VIO_OSC */
1578
+ case 0xb7: /* VDD1_OSC */
1579
+ case 0xc5: /* VDD2_OSC */
1580
+ s->reg_data[addr] = value & 0x5f;
1582
+ case 0xae: /* VIO_RESERVED */
1583
+ case 0xb8: /* VDD1_RESERVED */
1584
+ case 0xc6: /* VDD2_RESERVED */
1586
+ case 0xaf: /* VIO_VSEL */
1587
+ s->reg_data[addr] = value & 0x01;
1589
+ case 0xba: /* VDD1_VMODE_CFG */
1590
+ case 0xc8: /* VDD2_VMODE_CFG */
1591
+ s->reg_data[addr] = (s->reg_data[addr] & 0x38) | (value & 0x07);
1593
+ case 0xd8: /* VUSB_DEDICATED1 */
1594
+ s->reg_data[addr] = value & 0x1f;
1596
+ case 0xd9: /* VUSB_DEDICATED2 */
1597
+ s->reg_data[addr] = value & 0x08;
1601
+ hw_error("%s: unknown register 0x%02x value 0x%02x",
1602
+ __FUNCTION__, addr, value);
1607
+static void twl4030_key_setstate(TWL4030NodeState *s,
1608
+ int col, int row, int state)
1610
+ TRACE("col=%d, row=%d, state=%d", col, row, state);
1611
+ if (col >= 0 && col < 8 && row >= 0 && row < 8) {
1612
+ s->reg_data[0xd8] = col; /* KBC_REG */
1613
+ s->reg_data[0xd9] = row; /* KBR_REG */
1616
+ s->reg_data[0xdb + row] |= 1 << col; /* FULL_CODE_xx_yy */
1617
+ gen_int = s->reg_data[0xe8] & 0x02; /* ITKPRISING */
1619
+ s->reg_data[0xdb + row] &= ~(1 << col); /* FULL_CODE_xx_yy */
1620
+ gen_int = s->reg_data[0xe8] & 0x01; /* ITKPFALLING */
1623
+ s->reg_data[0xe3] |= 0x01; /* ITKPISR1 */
1624
+ s->reg_data[0xe5] |= 0x01; /* ITKPISR2 */
1625
+ twl4030_interrupt_update(s->twl4030);
1630
+static void twl4030_key_handler(void *opaque, int keycode)
1632
+ TWL4030NodeState *s = (TWL4030NodeState *)opaque;
1633
+ if (!s->twl4030->extended_key && keycode == 0xe0) {
1634
+ s->twl4030->extended_key = 0x80;
1636
+ const TWL4030KeyMap *k = s->twl4030->keymap;
1637
+ int fullcode = (keycode & 0x7f) | (s->twl4030->extended_key);
1638
+ for (; k && k->code >= 0; k++) {
1639
+ if (k->code == fullcode) {
1640
+ twl4030_key_setstate(s, k->column, k->row, !(keycode & 0x80));
1643
+ s->twl4030->extended_key = 0;
1647
+static void twl4030_node_reset(TWL4030NodeState *s,
1648
+ const uint8_t *reset_values)
1652
+ memcpy(s->reg_data, reset_values, 256);
1655
+static void twl4030_node_init(TWL4030NodeState *s,
1656
+ twl4030_read_func read,
1657
+ twl4030_write_func write,
1658
+ const uint8_t *reset_values)
1660
+ s->read_func = read;
1661
+ s->write_func = write;
1662
+ twl4030_node_reset(s, reset_values);
1665
+static int twl4030_48_init(I2CSlave *i2c)
1667
+ twl4030_node_init(FROM_I2C_SLAVE(TWL4030NodeState, i2c),
1668
+ twl4030_48_read, twl4030_48_write,
1669
+ addr_48_reset_values);
1673
+static int twl4030_49_init(I2CSlave *i2c)
1675
+ twl4030_node_init(FROM_I2C_SLAVE(TWL4030NodeState, i2c),
1676
+ twl4030_49_read, twl4030_49_write,
1677
+ addr_49_reset_values);
1681
+static int twl4030_4a_init(I2CSlave *i2c)
1683
+ TWL4030NodeState *s = FROM_I2C_SLAVE(TWL4030NodeState, i2c);
1684
+ twl4030_node_init(s,
1685
+ twl4030_4a_read, twl4030_4a_write,
1686
+ addr_4a_reset_values);
1687
+ qemu_add_kbd_event_handler(twl4030_key_handler, s);
1691
+static int twl4030_4b_init(I2CSlave *i2c)
1693
+ twl4030_node_init(FROM_I2C_SLAVE(TWL4030NodeState, i2c),
1694
+ twl4030_4b_read, twl4030_4b_write,
1695
+ addr_4b_reset_values);
1699
+static void twl4030_event(I2CSlave *i2c, enum i2c_event event)
1701
+ if (event == I2C_START_SEND) {
1702
+ TWL4030NodeState *s = FROM_I2C_SLAVE(TWL4030NodeState, i2c);
1707
+static int twl4030_rx(I2CSlave *i2c)
1709
+ TWL4030NodeState *s = FROM_I2C_SLAVE(TWL4030NodeState, i2c);
1710
+ return s->read_func(s, s->reg++);
1713
+static int twl4030_tx(I2CSlave *i2c, uint8_t data)
1715
+ TWL4030NodeState *s = FROM_I2C_SLAVE(TWL4030NodeState, i2c);
1716
+ if (s->firstbyte) {
1720
+ s->write_func(s, s->reg++, data);
1725
+static void twl4030_reset(void *opaque)
1727
+ TWL4030State *s = opaque;
1729
+ qemu_del_timer(s->alarm_timer);
1730
+ qemu_del_timer(s->periodic_timer);
1732
+ twl4030_node_reset(s->i2c[0], addr_48_reset_values);
1733
+ twl4030_node_reset(s->i2c[1], addr_49_reset_values);
1734
+ twl4030_node_reset(s->i2c[2], addr_4a_reset_values);
1735
+ twl4030_node_reset(s->i2c[3], addr_4b_reset_values);
1737
+ s->extended_key = 0;
1741
+ memset(s->seq_mem, 0, sizeof(s->seq_mem));
1743
+ /* TODO: indicate correct reset reason */
1746
+static void twl4030_48_class_init(ObjectClass *klass, void *data)
1748
+ I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
1749
+ k->init = twl4030_48_init;
1750
+ k->event = twl4030_event;
1751
+ k->recv = twl4030_rx;
1752
+ k->send = twl4030_tx;
1755
+static void twl4030_49_class_init(ObjectClass *klass, void *data)
1757
+ I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
1758
+ k->init = twl4030_49_init;
1759
+ k->event = twl4030_event;
1760
+ k->recv = twl4030_rx;
1761
+ k->send = twl4030_tx;
1764
+static void twl4030_4a_class_init(ObjectClass *klass, void *data)
1766
+ I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
1767
+ k->init = twl4030_4a_init;
1768
+ k->event = twl4030_event;
1769
+ k->recv = twl4030_rx;
1770
+ k->send = twl4030_tx;
1772
+static void twl4030_4b_class_init(ObjectClass *klass, void *data)
1774
+ I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
1775
+ k->init = twl4030_4b_init;
1776
+ k->event = twl4030_event;
1777
+ k->recv = twl4030_rx;
1778
+ k->send = twl4030_tx;
1781
+static TypeInfo twl4030_info[] = {
1783
+ .name = "twl4030_48",
1784
+ .parent = TYPE_I2C_SLAVE,
1785
+ .instance_size = sizeof(TWL4030NodeState),
1786
+ .class_init = twl4030_48_class_init,
1789
+ .name = "twl4030_49",
1790
+ .parent = TYPE_I2C_SLAVE,
1791
+ .instance_size = sizeof(TWL4030NodeState),
1792
+ .class_init = twl4030_49_class_init,
1795
+ .name = "twl4030_4a",
1796
+ .parent = TYPE_I2C_SLAVE,
1797
+ .instance_size = sizeof(TWL4030NodeState),
1798
+ .class_init = twl4030_4a_class_init,
1801
+ .name = "twl4030_4b",
1802
+ .parent = TYPE_I2C_SLAVE,
1803
+ .instance_size = sizeof(TWL4030NodeState),
1804
+ .class_init = twl4030_4b_class_init,
1808
+void *twl4030_init(i2c_bus *bus, qemu_irq irq1, qemu_irq irq2,
1809
+ const TWL4030KeyMap *keymap)
1811
+ TWL4030State *s = (TWL4030State *)g_malloc0(sizeof(*s));
1817
+ s->keymap = keymap;
1819
+ s->alarm_timer = qemu_new_timer_ns(vm_clock, twl4030_alarm, s);
1820
+ s->periodic_timer = qemu_new_timer_ns(vm_clock, twl4030_periodic, s);
1823
+ for (i = 0; i < ARRAY_SIZE(twl4030_info); i++) {
1824
+ DeviceState *ds = i2c_create_slave(bus, twl4030_info[i].name,
1826
+ s->i2c[i] = FROM_I2C_SLAVE(TWL4030NodeState, I2C_SLAVE(ds));
1827
+ s->i2c[i]->twl4030 = s;
1830
+ qemu_register_reset(twl4030_reset, s);
1834
+void *twl5031_init(i2c_bus *bus, qemu_irq irq1, qemu_irq irq2,
1835
+ const TWL4030KeyMap *keymap)
1837
+ TWL4030State *s = twl4030_init(bus, irq1, irq2, keymap);
1842
+void twl4030_set_powerbutton_state(void *opaque, int pressed)
1844
+ TWL4030State *s = opaque;
1846
+ if (!(s->i2c[3]->reg_data[0x45] & 0x01) && /* STS_PWON */
1847
+ (s->i2c[3]->reg_data[0x33] & 0x02)) { /* PWRON_RISING */
1848
+ s->i2c[3]->reg_data[0x2e] |= 0x01; /* PWRON */
1849
+ s->i2c[3]->reg_data[0x30] |= 0x01; /* PWRON */
1850
+ twl4030_interrupt_update(s);
1852
+ s->i2c[3]->reg_data[0x45] |= 0x01; /* STS_PWON */
1854
+ if ((s->i2c[3]->reg_data[0x45] & 0x01) && /* STS_PWON */
1855
+ (s->i2c[3]->reg_data[0x33] & 0x01)) { /* PWRON_FALLING */
1856
+ s->i2c[3]->reg_data[0x2e] |= 0x01; /* PWRON */
1857
+ s->i2c[3]->reg_data[0x30] |= 0x01; /* PWRON */
1858
+ twl4030_interrupt_update(s);
1860
+ s->i2c[3]->reg_data[0x45] &= ~0x01; /* STS_PWON */
1864
+void twl4030_madc_attach(void *opaque, twl4030_madc_callback cb)
1866
+ TWL4030State *s = opaque;
1869
+ "%s: warning - overriding previously registered callback\n",
1875
+static void twl4030_register_types(void)
1877
+ TypeInfo *p = twl4030_info;
1879
+ for (i = 0; i < ARRAY_SIZE(twl4030_info); p++, i++) {
1880
+ type_register_static(p);
1884
+type_init(twl4030_register_types);