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