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