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