|
1 /* |
|
2 * CBUS three-pin bus and the Retu / Betty / Tahvo / Vilma / Avilma / |
|
3 * Hinku / Vinku / Ahne / Pihi chips used in various Nokia platforms. |
|
4 * Based on reverse-engineering of a linux driver. |
|
5 * |
|
6 * Copyright (C) 2008 Nokia Corporation |
|
7 * Written by Andrzej Zaborowski <andrew@openedhand.com> |
|
8 * |
|
9 * This program is free software; you can redistribute it and/or |
|
10 * modify it under the terms of the GNU General Public License as |
|
11 * published by the Free Software Foundation; either version 2 or |
|
12 * (at your option) version 3 of the License. |
|
13 * |
|
14 * This program is distributed in the hope that it will be useful, |
|
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
17 * GNU General Public License for more details. |
|
18 * |
|
19 * You should have received a copy of the GNU General Public License |
|
20 * along with this program; if not, write to the Free Software |
|
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, |
|
22 * MA 02111-1307 USA |
|
23 */ |
|
24 |
|
25 #include "qemu-common.h" |
|
26 #include "irq.h" |
|
27 #include "devices.h" |
|
28 #include "sysemu.h" |
|
29 |
|
30 //#define DEBUG |
|
31 |
|
32 struct cbus_slave_s; |
|
33 struct cbus_priv_s { |
|
34 struct cbus_s cbus; |
|
35 |
|
36 int sel; |
|
37 int dat; |
|
38 int clk; |
|
39 int bit; |
|
40 int dir; |
|
41 uint16_t val; |
|
42 qemu_irq dat_out; |
|
43 |
|
44 int addr; |
|
45 int reg; |
|
46 int rw; |
|
47 enum { |
|
48 cbus_address, |
|
49 cbus_value, |
|
50 } cycle; |
|
51 |
|
52 struct cbus_slave_s *slave[8]; |
|
53 }; |
|
54 |
|
55 struct cbus_slave_s { |
|
56 void *opaque; |
|
57 void (*io)(void *opaque, int rw, int reg, uint16_t *val); |
|
58 int addr; |
|
59 }; |
|
60 |
|
61 static void cbus_io(struct cbus_priv_s *s) |
|
62 { |
|
63 if (s->slave[s->addr]) |
|
64 s->slave[s->addr]->io(s->slave[s->addr]->opaque, |
|
65 s->rw, s->reg, &s->val); |
|
66 else |
|
67 cpu_abort(cpu_single_env, "%s: bad slave address %i\n", |
|
68 __FUNCTION__, s->addr); |
|
69 } |
|
70 |
|
71 static void cbus_cycle(struct cbus_priv_s *s) |
|
72 { |
|
73 switch (s->cycle) { |
|
74 case cbus_address: |
|
75 s->addr = (s->val >> 6) & 7; |
|
76 s->rw = (s->val >> 5) & 1; |
|
77 s->reg = (s->val >> 0) & 0x1f; |
|
78 |
|
79 s->cycle = cbus_value; |
|
80 s->bit = 15; |
|
81 s->dir = !s->rw; |
|
82 s->val = 0; |
|
83 |
|
84 if (s->rw) |
|
85 cbus_io(s); |
|
86 break; |
|
87 |
|
88 case cbus_value: |
|
89 if (!s->rw) |
|
90 cbus_io(s); |
|
91 |
|
92 s->cycle = cbus_address; |
|
93 s->bit = 8; |
|
94 s->dir = 1; |
|
95 s->val = 0; |
|
96 break; |
|
97 } |
|
98 } |
|
99 |
|
100 static void cbus_clk(void *opaque, int line, int level) |
|
101 { |
|
102 struct cbus_priv_s *s = (struct cbus_priv_s *) opaque; |
|
103 |
|
104 if (!s->sel && level && !s->clk) { |
|
105 if (s->dir) |
|
106 s->val |= s->dat << (s->bit --); |
|
107 else |
|
108 qemu_set_irq(s->dat_out, (s->val >> (s->bit --)) & 1); |
|
109 |
|
110 if (s->bit < 0) |
|
111 cbus_cycle(s); |
|
112 } |
|
113 |
|
114 s->clk = level; |
|
115 } |
|
116 |
|
117 static void cbus_dat(void *opaque, int line, int level) |
|
118 { |
|
119 struct cbus_priv_s *s = (struct cbus_priv_s *) opaque; |
|
120 |
|
121 s->dat = level; |
|
122 } |
|
123 |
|
124 static void cbus_sel(void *opaque, int line, int level) |
|
125 { |
|
126 struct cbus_priv_s *s = (struct cbus_priv_s *) opaque; |
|
127 |
|
128 if (!level) { |
|
129 s->dir = 1; |
|
130 s->bit = 8; |
|
131 s->val = 0; |
|
132 } |
|
133 |
|
134 s->sel = level; |
|
135 } |
|
136 |
|
137 struct cbus_s *cbus_init(qemu_irq dat) |
|
138 { |
|
139 struct cbus_priv_s *s = (struct cbus_priv_s *) qemu_mallocz(sizeof(*s)); |
|
140 |
|
141 s->dat_out = dat; |
|
142 s->cbus.clk = qemu_allocate_irqs(cbus_clk, s, 1)[0]; |
|
143 s->cbus.dat = qemu_allocate_irqs(cbus_dat, s, 1)[0]; |
|
144 s->cbus.sel = qemu_allocate_irqs(cbus_sel, s, 1)[0]; |
|
145 |
|
146 s->sel = 1; |
|
147 s->clk = 0; |
|
148 s->dat = 0; |
|
149 |
|
150 return &s->cbus; |
|
151 } |
|
152 |
|
153 void cbus_attach(struct cbus_s *bus, void *slave_opaque) |
|
154 { |
|
155 struct cbus_slave_s *slave = (struct cbus_slave_s *) slave_opaque; |
|
156 struct cbus_priv_s *s = (struct cbus_priv_s *) bus; |
|
157 |
|
158 s->slave[slave->addr] = slave; |
|
159 } |
|
160 |
|
161 /* Retu/Vilma */ |
|
162 struct cbus_retu_s { |
|
163 uint16_t irqst; |
|
164 uint16_t irqen; |
|
165 uint16_t cc[2]; |
|
166 int channel; |
|
167 uint16_t result[16]; |
|
168 uint16_t sample; |
|
169 uint16_t status; |
|
170 |
|
171 struct { |
|
172 uint16_t cal; |
|
173 } rtc; |
|
174 |
|
175 int is_vilma; |
|
176 qemu_irq irq; |
|
177 struct cbus_slave_s cbus; |
|
178 }; |
|
179 |
|
180 static void retu_interrupt_update(struct cbus_retu_s *s) |
|
181 { |
|
182 qemu_set_irq(s->irq, s->irqst & ~s->irqen); |
|
183 } |
|
184 |
|
185 #define RETU_REG_ASICR 0x00 /* (RO) ASIC ID & revision */ |
|
186 #define RETU_REG_IDR 0x01 /* (T) Interrupt ID */ |
|
187 #define RETU_REG_IMR 0x02 /* (RW) Interrupt mask */ |
|
188 #define RETU_REG_RTCDSR 0x03 /* (RW) RTC seconds register */ |
|
189 #define RETU_REG_RTCHMR 0x04 /* (RO) RTC hours and minutes reg */ |
|
190 #define RETU_REG_RTCHMAR 0x05 /* (RW) RTC hours and minutes set reg */ |
|
191 #define RETU_REG_RTCCALR 0x06 /* (RW) RTC calibration register */ |
|
192 #define RETU_REG_ADCR 0x08 /* (RW) ADC result register */ |
|
193 #define RETU_REG_ADCSCR 0x09 /* (RW) ADC sample control register */ |
|
194 #define RETU_REG_AFCR 0x0a /* (RW) AFC register */ |
|
195 #define RETU_REG_ANTIFR 0x0b /* (RW) AntiF register */ |
|
196 #define RETU_REG_CALIBR 0x0c /* (RW) CalibR register*/ |
|
197 #define RETU_REG_CCR1 0x0d /* (RW) Common control register 1 */ |
|
198 #define RETU_REG_CCR2 0x0e /* (RW) Common control register 2 */ |
|
199 #define RETU_REG_RCTRL_CLR 0x0f /* (T) Regulator clear register */ |
|
200 #define RETU_REG_RCTRL_SET 0x10 /* (T) Regulator set register */ |
|
201 #define RETU_REG_TXCR 0x11 /* (RW) TxC register */ |
|
202 #define RETU_REG_STATUS 0x16 /* (RO) Status register */ |
|
203 #define RETU_REG_WATCHDOG 0x17 /* (RW) Watchdog register */ |
|
204 #define RETU_REG_AUDTXR 0x18 /* (RW) Audio Codec Tx register */ |
|
205 #define RETU_REG_AUDPAR 0x19 /* (RW) AudioPA register */ |
|
206 #define RETU_REG_AUDRXR1 0x1a /* (RW) Audio receive register 1 */ |
|
207 #define RETU_REG_AUDRXR2 0x1b /* (RW) Audio receive register 2 */ |
|
208 #define RETU_REG_SGR1 0x1c /* (RW) */ |
|
209 #define RETU_REG_SCR1 0x1d /* (RW) */ |
|
210 #define RETU_REG_SGR2 0x1e /* (RW) */ |
|
211 #define RETU_REG_SCR2 0x1f /* (RW) */ |
|
212 |
|
213 /* Retu Interrupt sources */ |
|
214 enum { |
|
215 retu_int_pwr = 0, /* Power button */ |
|
216 retu_int_char = 1, /* Charger */ |
|
217 retu_int_rtcs = 2, /* Seconds */ |
|
218 retu_int_rtcm = 3, /* Minutes */ |
|
219 retu_int_rtcd = 4, /* Days */ |
|
220 retu_int_rtca = 5, /* Alarm */ |
|
221 retu_int_hook = 6, /* Hook */ |
|
222 retu_int_head = 7, /* Headset */ |
|
223 retu_int_adcs = 8, /* ADC sample */ |
|
224 }; |
|
225 |
|
226 /* Retu ADC channel wiring */ |
|
227 enum { |
|
228 retu_adc_bsi = 1, /* BSI */ |
|
229 retu_adc_batt_temp = 2, /* Battery temperature */ |
|
230 retu_adc_chg_volt = 3, /* Charger voltage */ |
|
231 retu_adc_head_det = 4, /* Headset detection */ |
|
232 retu_adc_hook_det = 5, /* Hook detection */ |
|
233 retu_adc_rf_gp = 6, /* RF GP */ |
|
234 retu_adc_tx_det = 7, /* Wideband Tx detection */ |
|
235 retu_adc_batt_volt = 8, /* Battery voltage */ |
|
236 retu_adc_sens = 10, /* Light sensor */ |
|
237 retu_adc_sens_temp = 11, /* Light sensor temperature */ |
|
238 retu_adc_bbatt_volt = 12, /* Backup battery voltage */ |
|
239 retu_adc_self_temp = 13, /* RETU temperature */ |
|
240 }; |
|
241 |
|
242 static inline uint16_t retu_read(struct cbus_retu_s *s, int reg) |
|
243 { |
|
244 #ifdef DEBUG |
|
245 printf("RETU read at %02x\n", reg); |
|
246 #endif |
|
247 |
|
248 switch (reg) { |
|
249 case RETU_REG_ASICR: |
|
250 return 0x0215 | (s->is_vilma << 7); |
|
251 |
|
252 case RETU_REG_IDR: /* TODO: Or is this ffs(s->irqst)? */ |
|
253 return s->irqst; |
|
254 |
|
255 case RETU_REG_IMR: |
|
256 return s->irqen; |
|
257 |
|
258 case RETU_REG_RTCDSR: |
|
259 case RETU_REG_RTCHMR: |
|
260 case RETU_REG_RTCHMAR: |
|
261 /* TODO */ |
|
262 return 0x0000; |
|
263 |
|
264 case RETU_REG_RTCCALR: |
|
265 return s->rtc.cal; |
|
266 |
|
267 case RETU_REG_ADCR: |
|
268 return (s->channel << 10) | s->result[s->channel]; |
|
269 case RETU_REG_ADCSCR: |
|
270 return s->sample; |
|
271 |
|
272 case RETU_REG_AFCR: |
|
273 case RETU_REG_ANTIFR: |
|
274 case RETU_REG_CALIBR: |
|
275 /* TODO */ |
|
276 return 0x0000; |
|
277 |
|
278 case RETU_REG_CCR1: |
|
279 return s->cc[0]; |
|
280 case RETU_REG_CCR2: |
|
281 return s->cc[1]; |
|
282 |
|
283 case RETU_REG_RCTRL_CLR: |
|
284 case RETU_REG_RCTRL_SET: |
|
285 case RETU_REG_TXCR: |
|
286 /* TODO */ |
|
287 return 0x0000; |
|
288 |
|
289 case RETU_REG_STATUS: |
|
290 return s->status; |
|
291 |
|
292 case RETU_REG_WATCHDOG: |
|
293 case RETU_REG_AUDTXR: |
|
294 case RETU_REG_AUDPAR: |
|
295 case RETU_REG_AUDRXR1: |
|
296 case RETU_REG_AUDRXR2: |
|
297 case RETU_REG_SGR1: |
|
298 case RETU_REG_SCR1: |
|
299 case RETU_REG_SGR2: |
|
300 case RETU_REG_SCR2: |
|
301 /* TODO */ |
|
302 return 0x0000; |
|
303 |
|
304 default: |
|
305 cpu_abort(cpu_single_env, "%s: bad register %02x\n", |
|
306 __FUNCTION__, reg); |
|
307 } |
|
308 } |
|
309 |
|
310 static inline void retu_write(struct cbus_retu_s *s, int reg, uint16_t val) |
|
311 { |
|
312 #ifdef DEBUG |
|
313 printf("RETU write of %04x at %02x\n", val, reg); |
|
314 #endif |
|
315 |
|
316 switch (reg) { |
|
317 case RETU_REG_IDR: |
|
318 s->irqst ^= val; |
|
319 retu_interrupt_update(s); |
|
320 break; |
|
321 |
|
322 case RETU_REG_IMR: |
|
323 s->irqen = val; |
|
324 retu_interrupt_update(s); |
|
325 break; |
|
326 |
|
327 case RETU_REG_RTCDSR: |
|
328 case RETU_REG_RTCHMAR: |
|
329 /* TODO */ |
|
330 break; |
|
331 |
|
332 case RETU_REG_RTCCALR: |
|
333 s->rtc.cal = val; |
|
334 break; |
|
335 |
|
336 case RETU_REG_ADCR: |
|
337 s->channel = (val >> 10) & 0xf; |
|
338 s->irqst |= 1 << retu_int_adcs; |
|
339 retu_interrupt_update(s); |
|
340 break; |
|
341 case RETU_REG_ADCSCR: |
|
342 s->sample &= ~val; |
|
343 break; |
|
344 |
|
345 case RETU_REG_AFCR: |
|
346 case RETU_REG_ANTIFR: |
|
347 case RETU_REG_CALIBR: |
|
348 |
|
349 case RETU_REG_CCR1: |
|
350 s->cc[0] = val; |
|
351 break; |
|
352 case RETU_REG_CCR2: |
|
353 s->cc[1] = val; |
|
354 break; |
|
355 |
|
356 case RETU_REG_RCTRL_CLR: |
|
357 case RETU_REG_RCTRL_SET: |
|
358 /* TODO */ |
|
359 break; |
|
360 |
|
361 case RETU_REG_WATCHDOG: |
|
362 if (val == 0 && (s->cc[0] & 2)) |
|
363 qemu_system_shutdown_request(); |
|
364 break; |
|
365 |
|
366 case RETU_REG_TXCR: |
|
367 case RETU_REG_AUDTXR: |
|
368 case RETU_REG_AUDPAR: |
|
369 case RETU_REG_AUDRXR1: |
|
370 case RETU_REG_AUDRXR2: |
|
371 case RETU_REG_SGR1: |
|
372 case RETU_REG_SCR1: |
|
373 case RETU_REG_SGR2: |
|
374 case RETU_REG_SCR2: |
|
375 /* TODO */ |
|
376 break; |
|
377 |
|
378 default: |
|
379 cpu_abort(cpu_single_env, "%s: bad register %02x\n", |
|
380 __FUNCTION__, reg); |
|
381 } |
|
382 } |
|
383 |
|
384 static void retu_io(void *opaque, int rw, int reg, uint16_t *val) |
|
385 { |
|
386 struct cbus_retu_s *s = (struct cbus_retu_s *) opaque; |
|
387 |
|
388 if (rw) |
|
389 *val = retu_read(s, reg); |
|
390 else |
|
391 retu_write(s, reg, *val); |
|
392 } |
|
393 |
|
394 void *retu_init(qemu_irq irq, int vilma) |
|
395 { |
|
396 struct cbus_retu_s *s = (struct cbus_retu_s *) qemu_mallocz(sizeof(*s)); |
|
397 |
|
398 s->irq = irq; |
|
399 s->irqen = 0xffff; |
|
400 s->irqst = 0x0000; |
|
401 s->status = 0x0020; |
|
402 s->is_vilma = !!vilma; |
|
403 s->rtc.cal = 0x01; |
|
404 s->result[retu_adc_bsi] = 0x3c2; |
|
405 s->result[retu_adc_batt_temp] = 0x0fc; |
|
406 s->result[retu_adc_chg_volt] = 0x165; |
|
407 s->result[retu_adc_head_det] = 123; |
|
408 s->result[retu_adc_hook_det] = 1023; |
|
409 s->result[retu_adc_rf_gp] = 0x11; |
|
410 s->result[retu_adc_tx_det] = 0x11; |
|
411 s->result[retu_adc_batt_volt] = 0x250; |
|
412 s->result[retu_adc_sens] = 2; |
|
413 s->result[retu_adc_sens_temp] = 0x11; |
|
414 s->result[retu_adc_bbatt_volt] = 0x3d0; |
|
415 s->result[retu_adc_self_temp] = 0x330; |
|
416 |
|
417 s->cbus.opaque = s; |
|
418 s->cbus.io = retu_io; |
|
419 s->cbus.addr = 1; |
|
420 |
|
421 return &s->cbus; |
|
422 } |
|
423 |
|
424 void retu_key_event(void *retu, int state) |
|
425 { |
|
426 struct cbus_slave_s *slave = (struct cbus_slave_s *) retu; |
|
427 struct cbus_retu_s *s = (struct cbus_retu_s *) slave->opaque; |
|
428 |
|
429 s->irqst |= 1 << retu_int_pwr; |
|
430 retu_interrupt_update(s); |
|
431 |
|
432 if (state) |
|
433 s->status &= ~(1 << 5); |
|
434 else |
|
435 s->status |= 1 << 5; |
|
436 } |
|
437 |
|
438 #if 0 |
|
439 static void retu_head_event(void *retu, int state) |
|
440 { |
|
441 struct cbus_slave_s *slave = (struct cbus_slave_s *) retu; |
|
442 struct cbus_retu_s *s = (struct cbus_retu_s *) slave->opaque; |
|
443 |
|
444 if ((s->cc[0] & 0x500) == 0x500) { /* TODO: Which bits? */ |
|
445 /* TODO: reissue the interrupt every 100ms or so. */ |
|
446 s->irqst |= 1 << retu_int_head; |
|
447 retu_interrupt_update(s); |
|
448 } |
|
449 |
|
450 if (state) |
|
451 s->result[retu_adc_head_det] = 50; |
|
452 else |
|
453 s->result[retu_adc_head_det] = 123; |
|
454 } |
|
455 |
|
456 static void retu_hook_event(void *retu, int state) |
|
457 { |
|
458 struct cbus_slave_s *slave = (struct cbus_slave_s *) retu; |
|
459 struct cbus_retu_s *s = (struct cbus_retu_s *) slave->opaque; |
|
460 |
|
461 if ((s->cc[0] & 0x500) == 0x500) { |
|
462 /* TODO: reissue the interrupt every 100ms or so. */ |
|
463 s->irqst |= 1 << retu_int_hook; |
|
464 retu_interrupt_update(s); |
|
465 } |
|
466 |
|
467 if (state) |
|
468 s->result[retu_adc_hook_det] = 50; |
|
469 else |
|
470 s->result[retu_adc_hook_det] = 123; |
|
471 } |
|
472 #endif |
|
473 |
|
474 /* Tahvo/Betty */ |
|
475 struct cbus_tahvo_s { |
|
476 uint16_t irqst; |
|
477 uint16_t irqen; |
|
478 uint8_t charger; |
|
479 uint8_t backlight; |
|
480 uint16_t usbr; |
|
481 uint16_t power; |
|
482 |
|
483 int is_betty; |
|
484 qemu_irq irq; |
|
485 struct cbus_slave_s cbus; |
|
486 }; |
|
487 |
|
488 static void tahvo_interrupt_update(struct cbus_tahvo_s *s) |
|
489 { |
|
490 qemu_set_irq(s->irq, s->irqst & ~s->irqen); |
|
491 } |
|
492 |
|
493 #define TAHVO_REG_ASICR 0x00 /* (RO) ASIC ID & revision */ |
|
494 #define TAHVO_REG_IDR 0x01 /* (T) Interrupt ID */ |
|
495 #define TAHVO_REG_IDSR 0x02 /* (RO) Interrupt status */ |
|
496 #define TAHVO_REG_IMR 0x03 /* (RW) Interrupt mask */ |
|
497 #define TAHVO_REG_CHAPWMR 0x04 /* (RW) Charger PWM */ |
|
498 #define TAHVO_REG_LEDPWMR 0x05 /* (RW) LED PWM */ |
|
499 #define TAHVO_REG_USBR 0x06 /* (RW) USB control */ |
|
500 #define TAHVO_REG_RCR 0x07 /* (RW) Some kind of power management */ |
|
501 #define TAHVO_REG_CCR1 0x08 /* (RW) Common control register 1 */ |
|
502 #define TAHVO_REG_CCR2 0x09 /* (RW) Common control register 2 */ |
|
503 #define TAHVO_REG_TESTR1 0x0a /* (RW) Test register 1 */ |
|
504 #define TAHVO_REG_TESTR2 0x0b /* (RW) Test register 2 */ |
|
505 #define TAHVO_REG_NOPR 0x0c /* (RW) Number of periods */ |
|
506 #define TAHVO_REG_FRR 0x0d /* (RO) FR */ |
|
507 |
|
508 static inline uint16_t tahvo_read(struct cbus_tahvo_s *s, int reg) |
|
509 { |
|
510 #ifdef DEBUG |
|
511 printf("TAHVO read at %02x\n", reg); |
|
512 #endif |
|
513 |
|
514 switch (reg) { |
|
515 case TAHVO_REG_ASICR: |
|
516 return 0x0021 | (s->is_betty ? 0x0b00 : 0x0300); /* 22 in N810 */ |
|
517 |
|
518 case TAHVO_REG_IDR: |
|
519 case TAHVO_REG_IDSR: /* XXX: what does this do? */ |
|
520 return s->irqst; |
|
521 |
|
522 case TAHVO_REG_IMR: |
|
523 return s->irqen; |
|
524 |
|
525 case TAHVO_REG_CHAPWMR: |
|
526 return s->charger; |
|
527 |
|
528 case TAHVO_REG_LEDPWMR: |
|
529 return s->backlight; |
|
530 |
|
531 case TAHVO_REG_USBR: |
|
532 return s->usbr; |
|
533 |
|
534 case TAHVO_REG_RCR: |
|
535 return s->power; |
|
536 |
|
537 case TAHVO_REG_CCR1: |
|
538 case TAHVO_REG_CCR2: |
|
539 case TAHVO_REG_TESTR1: |
|
540 case TAHVO_REG_TESTR2: |
|
541 case TAHVO_REG_NOPR: |
|
542 case TAHVO_REG_FRR: |
|
543 return 0x0000; |
|
544 |
|
545 default: |
|
546 cpu_abort(cpu_single_env, "%s: bad register %02x\n", |
|
547 __FUNCTION__, reg); |
|
548 } |
|
549 } |
|
550 |
|
551 static inline void tahvo_write(struct cbus_tahvo_s *s, int reg, uint16_t val) |
|
552 { |
|
553 #ifdef DEBUG |
|
554 printf("TAHVO write of %04x at %02x\n", val, reg); |
|
555 #endif |
|
556 |
|
557 switch (reg) { |
|
558 case TAHVO_REG_IDR: |
|
559 s->irqst ^= val; |
|
560 tahvo_interrupt_update(s); |
|
561 break; |
|
562 |
|
563 case TAHVO_REG_IMR: |
|
564 s->irqen = val; |
|
565 tahvo_interrupt_update(s); |
|
566 break; |
|
567 |
|
568 case TAHVO_REG_CHAPWMR: |
|
569 s->charger = val; |
|
570 break; |
|
571 |
|
572 case TAHVO_REG_LEDPWMR: |
|
573 if (s->backlight != (val & 0x7f)) { |
|
574 s->backlight = val & 0x7f; |
|
575 printf("%s: LCD backlight now at %i / 127\n", |
|
576 __FUNCTION__, s->backlight); |
|
577 } |
|
578 break; |
|
579 |
|
580 case TAHVO_REG_USBR: |
|
581 s->usbr = val; |
|
582 break; |
|
583 |
|
584 case TAHVO_REG_RCR: |
|
585 s->power = val; |
|
586 break; |
|
587 |
|
588 case TAHVO_REG_CCR1: |
|
589 case TAHVO_REG_CCR2: |
|
590 case TAHVO_REG_TESTR1: |
|
591 case TAHVO_REG_TESTR2: |
|
592 case TAHVO_REG_NOPR: |
|
593 case TAHVO_REG_FRR: |
|
594 break; |
|
595 |
|
596 default: |
|
597 cpu_abort(cpu_single_env, "%s: bad register %02x\n", |
|
598 __FUNCTION__, reg); |
|
599 } |
|
600 } |
|
601 |
|
602 static void tahvo_io(void *opaque, int rw, int reg, uint16_t *val) |
|
603 { |
|
604 struct cbus_tahvo_s *s = (struct cbus_tahvo_s *) opaque; |
|
605 |
|
606 if (rw) |
|
607 *val = tahvo_read(s, reg); |
|
608 else |
|
609 tahvo_write(s, reg, *val); |
|
610 } |
|
611 |
|
612 void *tahvo_init(qemu_irq irq, int betty) |
|
613 { |
|
614 struct cbus_tahvo_s *s = (struct cbus_tahvo_s *) qemu_mallocz(sizeof(*s)); |
|
615 |
|
616 s->irq = irq; |
|
617 s->irqen = 0xffff; |
|
618 s->irqst = 0x0000; |
|
619 s->is_betty = !!betty; |
|
620 |
|
621 s->cbus.opaque = s; |
|
622 s->cbus.io = tahvo_io; |
|
623 s->cbus.addr = 2; |
|
624 |
|
625 return &s->cbus; |
|
626 } |