Tizen 2.1 base
[sdk/emulator/qemu.git] / hw / ak8973.c
1 /*
2  * AK8973 Compass Emulation
3  *
4  * Copyright (c) 2010 Samsung Electronics.
5  * Contributed by Junsik.Park <okdear.park@samsung.com>
6  */
7
8 #include "i2c-addressable.h"
9 #include "ak8973.h"
10
11 //#define DEBUG
12
13 #define AK8973_ST               0xC0
14 #define AK8973_TMPS             0xC1
15 #define AK8973_H1X              0xC2
16 #define AK8973_H1Y              0xC3
17 #define AK8973_H1Z              0xC4
18
19 #define AK8973_MS1              0xE0
20 #define AK8973_HXDA             0xE1
21 #define AK8973_HYDA             0xE2
22 #define AK8973_HZDA             0xE3
23 #define AK8973_HXGA             0xE4
24 #define AK8973_HYGA             0xE5
25 #define AK8973_HZGA             0xE6
26
27 #define AK8973_ETS              0x62
28 #define AK8973_EVIR             0x63
29 #define AK8973_EIHE             0x64
30 #define AK8973_ETST             0x65
31 #define AK8973_EHXGA            0x66
32 #define AK8973_EHYGA            0x67
33 #define AK8973_EHZGA            0x68
34 #define AK8973_WRAL1            0x60
35
36
37 typedef struct AK8973State {
38     I2CAddressableState i2c_addressable;
39
40     uint8_t status;
41     uint8_t x;
42     uint8_t y;
43     uint8_t z;
44         uint8_t temp;
45     uint8_t ms1;
46     uint8_t dac[3];
47     uint8_t gain[3];
48     qemu_irq irq;
49 } AK8973State;
50
51
52 static void ak8973_reset(DeviceState *d)
53 {
54     AK8973State *s =
55         FROM_I2CADDR_SLAVE(AK8973State, I2CADDR_SLAVE_FROM_QDEV(d));
56
57     s->status  = 0;
58     /* Random coordinates */
59     s->x       = 10;
60     s->y       = 20;
61     s->z       = 30;
62
63     /* 20 degree Celsius */
64         s->temp    = 0x90;
65
66     /* EEPROM data-write disable / Powerdown mode */
67     s->ms1     = 0x3;
68
69     /* TODO: get the defaults */
70     s->dac[0]  = 0;
71     s->dac[1]  = 0;
72     s->dac[2]  = 0;
73     s->gain[0] = 0;
74     s->gain[1] = 0;
75     s->gain[2] = 0;
76
77     return;
78 }
79
80 static uint8_t ak8973_read(void *opaque, uint32_t address, uint8_t offset)
81 {
82     struct AK8973State *s = (struct AK8973State *)opaque;
83     uint32_t ret = 0, index = address + offset;
84
85     switch (index) {
86     case AK8973_ST:
87         ret = s->status;
88         break;
89     case AK8973_TMPS:
90         /* IRQ is lowered when any of data registers are read */
91         qemu_irq_lower(s->irq);
92         s->status &= ~1;
93         ret = s->temp;
94         break;
95     case AK8973_H1X:
96         qemu_irq_lower(s->irq);
97         s->status &= ~1;
98         ret = s->x;
99         break;
100     case AK8973_H1Y:
101         qemu_irq_lower(s->irq);
102         s->status &= ~1;
103         ret = s->y;
104         break;
105     case AK8973_H1Z:
106         qemu_irq_lower(s->irq);
107         s->status &= ~1;
108         ret = s->z;
109         break;
110     case AK8973_MS1:
111         ret = s->ms1;
112         break;
113     case AK8973_HXDA:
114     case AK8973_HYDA:
115     case AK8973_HZDA:
116         ret = s->dac[index - AK8973_HXDA];
117         break;
118     case AK8973_HXGA:
119     case AK8973_HYGA:
120     case AK8973_HZGA:
121         ret = s->gain[index - AK8973_HXGA];
122         break;
123     case AK8973_ETS:
124     case AK8973_EVIR:
125     case AK8973_EIHE:
126     case AK8973_ETST:
127     case AK8973_EHXGA:
128     case AK8973_EHYGA:
129     case AK8973_EHZGA:
130     case AK8973_WRAL1:
131         if ((s->ms1 & 3) == 2 && (s->ms1 & 0xF8) != 0xA8) {
132             /* TODO: implement EEPROM reading */
133             ret = 0;
134         } else {
135             ret = 0;
136         }
137         break;
138     default:
139         hw_error("ak8973: bad read offset 0x%x\n", index);
140     }
141
142 #ifdef DEBUG
143     printf("ak8973_read IDX = 0x%x, Data = 0x%x\n", index, ret);
144 #endif
145
146     return ret;
147 }
148
149 static void ak8973_write(void *opaque, uint32_t address, uint8_t offset,
150                          uint8_t val)
151 {
152     struct AK8973State *s = (struct AK8973State *)opaque;
153     uint32_t index = address + offset;
154
155 #ifdef DEBUG
156     printf("ak8973_write IDX = 0x%x, Data = 0x%x\n", index, val);
157 #endif
158
159     switch (index) {
160     case AK8973_MS1:
161         if ((val & 3) == 2) {
162             /* EEPROM access mode */
163             /* TODO: implement this mode */
164             s->ms1 = val;
165         } else {
166             /* All other mode setting finishes in power-down mode */
167             s->ms1 |= 3;
168         }
169         if ((val & 3) == 0) {
170             /* Measurement mode */
171             qemu_irq_raise(s->irq);
172             s->status |= 1;
173             /* TODO: change measurement registers accordingly */
174         }
175         break;
176     case AK8973_HXDA:
177     case AK8973_HYDA:
178     case AK8973_HZDA:
179         /* TODO: implement DAC changes influence on reported data */
180         s->dac[index - AK8973_HXDA] = val;
181         break;
182     case AK8973_HXGA:
183     case AK8973_HYGA:
184     case AK8973_HZGA:
185         /* TODO: implement gain changes influence on reported data */
186         s->gain[index - AK8973_HXGA] = val;
187         break;
188     case AK8973_ETS:
189     case AK8973_EVIR:
190     case AK8973_EIHE:
191     case AK8973_ETST:
192     case AK8973_EHXGA:
193     case AK8973_EHYGA:
194     case AK8973_EHZGA:
195     case AK8973_WRAL1:
196         if ((s->ms1 & 3) == 2 && (s->ms1 & 0xF8) == 0xA8) {
197             /* TODO: implement EEPROM writing */
198         }
199         break;
200     default:
201         hw_error("ak8973: bad write offset 0x%x\n", index);
202     }
203 }
204
205 static void ak8973_save(QEMUFile *f, void *opaque)
206 {
207     struct AK8973State *s = (struct AK8973State *)opaque;
208
209     qemu_put_8s(f, &s->status);
210     qemu_put_8s(f, &s->ms1);
211     qemu_put_buffer(f, s->dac, 3);
212     qemu_put_buffer(f, s->gain, 3);
213 }
214
215 static int ak8973_load(QEMUFile *f, void *opaque, int version_id)
216 {
217     struct AK8973State *s = (struct AK8973State *)opaque;
218
219     if (version_id != 1) {
220         return -EINVAL;
221     }
222
223     qemu_get_8s(f, &s->status);
224     qemu_get_8s(f, &s->ms1);
225     qemu_get_buffer(f, s->dac, 3);
226     qemu_get_buffer(f, s->gain, 3);
227
228     return 0;
229 }
230
231 void ak8973_update(DeviceState *dev,
232                    uint8_t x, uint8_t y, uint8_t z, uint8_t temp)
233 {
234         AK8973State *s =
235         FROM_I2CADDR_SLAVE(AK8973State, I2CADDR_SLAVE_FROM_QDEV(dev));
236     s->x = x;
237     s->y = y;
238     s->z = z;
239     s->temp = temp;
240 #ifdef DEBUG
241     printf("compass update : %d %d %d %d\n", x, y, z, temp);
242 #endif
243 }
244
245
246 static int ak8973_init(I2CAddressableState *s)
247 {
248     AK8973State *t = FROM_I2CADDR_SLAVE(AK8973State, s);
249
250     /* Set irq address */
251     qdev_init_gpio_out(&s->i2c.qdev, &t->irq, 1);
252
253     ak8973_reset(&s->i2c.qdev);
254
255     register_savevm(&s->i2c.qdev, "ak8973", -1, 1,
256                     ak8973_save, ak8973_load, s);
257
258     return 0;
259 }
260
261 static I2CAddressableDeviceInfo ak8973_info = {
262     .i2c.qdev.name = "ak8973",
263     .i2c.qdev.size = sizeof(AK8973State),
264     .i2c.qdev.reset = ak8973_reset,
265     .init = ak8973_init,
266     .read = ak8973_read,
267     .write = ak8973_write,
268     .size = 1,
269     .rev = 0
270 };
271
272 static void ak8973_register_devices(void)
273 {
274     i2c_addressable_register_device(&ak8973_info);
275 }
276
277 device_init(ak8973_register_devices)