xref: /illumos-kvm-cmd/hw/cbus.c (revision 68396ea9)
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 along
20  * with this program; if not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 #include "qemu-common.h"
24 #include "irq.h"
25 #include "devices.h"
26 #include "sysemu.h"
27 
28 //#define DEBUG
29 
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;
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 
55     CBusSlave *slave[8];
56 } CBusPriv;
57 
cbus_io(CBusPriv * s)58 static void cbus_io(CBusPriv *s)
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
64         hw_error("%s: bad slave address %i\n", __FUNCTION__, s->addr);
65 }
66 
cbus_cycle(CBusPriv * s)67 static void cbus_cycle(CBusPriv *s)
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 
cbus_clk(void * opaque,int line,int level)96 static void cbus_clk(void *opaque, int line, int level)
97 {
98     CBusPriv *s = (CBusPriv *) opaque;
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 
cbus_dat(void * opaque,int line,int level)113 static void cbus_dat(void *opaque, int line, int level)
114 {
115     CBusPriv *s = (CBusPriv *) opaque;
116 
117     s->dat = level;
118 }
119 
cbus_sel(void * opaque,int line,int level)120 static void cbus_sel(void *opaque, int line, int level)
121 {
122     CBusPriv *s = (CBusPriv *) opaque;
123 
124     if (!level) {
125         s->dir = 1;
126         s->bit = 8;
127         s->val = 0;
128     }
129 
130     s->sel = level;
131 }
132 
cbus_init(qemu_irq dat)133 CBus *cbus_init(qemu_irq dat)
134 {
135     CBusPriv *s = (CBusPriv *) qemu_mallocz(sizeof(*s));
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 
cbus_attach(CBus * bus,void * slave_opaque)149 void cbus_attach(CBus *bus, void *slave_opaque)
150 {
151     CBusSlave *slave = (CBusSlave *) slave_opaque;
152     CBusPriv *s = (CBusPriv *) bus;
153 
154     s->slave[slave->addr] = slave;
155 }
156 
157 /* Retu/Vilma */
158 typedef struct {
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;
173     CBusSlave cbus;
174 } CBusRetu;
175 
retu_interrupt_update(CBusRetu * s)176 static void retu_interrupt_update(CBusRetu *s)
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 
retu_read(CBusRetu * s,int reg)238 static inline uint16_t retu_read(CBusRetu *s, int reg)
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:
301         hw_error("%s: bad register %02x\n", __FUNCTION__, reg);
302     }
303 }
304 
retu_write(CBusRetu * s,int reg,uint16_t val)305 static inline void retu_write(CBusRetu *s, int reg, uint16_t val)
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:
374         hw_error("%s: bad register %02x\n", __FUNCTION__, reg);
375     }
376 }
377 
retu_io(void * opaque,int rw,int reg,uint16_t * val)378 static void retu_io(void *opaque, int rw, int reg, uint16_t *val)
379 {
380     CBusRetu *s = (CBusRetu *) opaque;
381 
382     if (rw)
383         *val = retu_read(s, reg);
384     else
385         retu_write(s, reg, *val);
386 }
387 
retu_init(qemu_irq irq,int vilma)388 void *retu_init(qemu_irq irq, int vilma)
389 {
390     CBusRetu *s = (CBusRetu *) qemu_mallocz(sizeof(*s));
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 
retu_key_event(void * retu,int state)418 void retu_key_event(void *retu, int state)
419 {
420     CBusSlave *slave = (CBusSlave *) retu;
421     CBusRetu *s = (CBusRetu *) slave->opaque;
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 {
435     CBusSlave *slave = (CBusSlave *) retu;
436     CBusRetu *s = (CBusRetu *) slave->opaque;
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 {
452     CBusSlave *slave = (CBusSlave *) retu;
453     CBusRetu *s = (CBusRetu *) slave->opaque;
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 */
469 typedef struct {
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;
479     CBusSlave cbus;
480 } CBusTahvo;
481 
tahvo_interrupt_update(CBusTahvo * s)482 static void tahvo_interrupt_update(CBusTahvo *s)
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 
tahvo_read(CBusTahvo * s,int reg)502 static inline uint16_t tahvo_read(CBusTahvo *s, int reg)
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:
540         hw_error("%s: bad register %02x\n", __FUNCTION__, reg);
541     }
542 }
543 
tahvo_write(CBusTahvo * s,int reg,uint16_t val)544 static inline void tahvo_write(CBusTahvo *s, int reg, uint16_t val)
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:
590         hw_error("%s: bad register %02x\n", __FUNCTION__, reg);
591     }
592 }
593 
tahvo_io(void * opaque,int rw,int reg,uint16_t * val)594 static void tahvo_io(void *opaque, int rw, int reg, uint16_t *val)
595 {
596     CBusTahvo *s = (CBusTahvo *) opaque;
597 
598     if (rw)
599         *val = tahvo_read(s, reg);
600     else
601         tahvo_write(s, reg, *val);
602 }
603 
tahvo_init(qemu_irq irq,int betty)604 void *tahvo_init(qemu_irq irq, int betty)
605 {
606     CBusTahvo *s = (CBusTahvo *) qemu_mallocz(sizeof(*s));
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 }
619