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