| 
									
										
										
										
											2008-04-14 21:57:44 +00:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * CBUS three-pin bus and the Retu / Betty / Tahvo / Vilma / Avilma / | 
					
						
							|  |  |  |  * Hinku / Vinku / Ahne / Pihi chips used in various Nokia platforms. | 
					
						
							|  |  |  |  * Based on reverse-engineering of a linux driver. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright (C) 2008 Nokia Corporation | 
					
						
							|  |  |  |  * Written by Andrzej Zaborowski <andrew@openedhand.com> | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is free software; you can redistribute it and/or | 
					
						
							|  |  |  |  * modify it under the terms of the GNU General Public License as | 
					
						
							|  |  |  |  * published by the Free Software Foundation; either version 2 or | 
					
						
							|  |  |  |  * (at your option) version 3 of the License. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is distributed in the hope that it will be useful, | 
					
						
							|  |  |  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
					
						
							|  |  |  |  * GNU General Public License for more details. | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2009-01-04 22:05:52 +00:00
										 |  |  |  * You should have received a copy of the GNU General Public License along | 
					
						
							| 
									
										
										
										
											2009-07-16 20:47:01 +00:00
										 |  |  |  * with this program; if not, see <http://www.gnu.org/licenses/>.
 | 
					
						
							| 
									
										
										
										
											2008-04-14 21:57:44 +00:00
										 |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-26 18:17:17 +00:00
										 |  |  | #include "qemu/osdep.h"
 | 
					
						
							| 
									
										
										
										
											2016-03-09 12:19:37 +01:00
										 |  |  | #include "hw/hw.h"
 | 
					
						
							| 
									
										
										
										
											2013-02-04 15:40:22 +01:00
										 |  |  | #include "hw/irq.h"
 | 
					
						
							| 
									
										
										
										
											2013-04-09 15:26:55 +01:00
										 |  |  | #include "hw/devices.h"
 | 
					
						
							| 
									
										
										
										
											2012-12-17 18:20:04 +01:00
										 |  |  | #include "sysemu/sysemu.h"
 | 
					
						
							| 
									
										
										
										
											2008-04-14 21:57:44 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | //#define DEBUG
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-10 01:44:56 +01:00
										 |  |  | typedef struct { | 
					
						
							|  |  |  |     void *opaque; | 
					
						
							|  |  |  |     void (*io)(void *opaque, int rw, int reg, uint16_t *val); | 
					
						
							|  |  |  |     int addr; | 
					
						
							|  |  |  | } CBusSlave; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | typedef struct { | 
					
						
							|  |  |  |     CBus cbus; | 
					
						
							| 
									
										
										
										
											2008-04-14 21:57:44 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     int sel; | 
					
						
							|  |  |  |     int dat; | 
					
						
							|  |  |  |     int clk; | 
					
						
							|  |  |  |     int bit; | 
					
						
							|  |  |  |     int dir; | 
					
						
							|  |  |  |     uint16_t val; | 
					
						
							|  |  |  |     qemu_irq dat_out; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     int addr; | 
					
						
							|  |  |  |     int reg; | 
					
						
							|  |  |  |     int rw; | 
					
						
							|  |  |  |     enum { | 
					
						
							|  |  |  |         cbus_address, | 
					
						
							|  |  |  |         cbus_value, | 
					
						
							|  |  |  |     } cycle; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-10 01:44:56 +01:00
										 |  |  |     CBusSlave *slave[8]; | 
					
						
							|  |  |  | } CBusPriv; | 
					
						
							| 
									
										
										
										
											2008-04-14 21:57:44 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-10 01:44:56 +01:00
										 |  |  | static void cbus_io(CBusPriv *s) | 
					
						
							| 
									
										
										
										
											2008-04-14 21:57:44 +00:00
										 |  |  | { | 
					
						
							|  |  |  |     if (s->slave[s->addr]) | 
					
						
							|  |  |  |         s->slave[s->addr]->io(s->slave[s->addr]->opaque, | 
					
						
							|  |  |  |                         s->rw, s->reg, &s->val); | 
					
						
							|  |  |  |     else | 
					
						
							| 
									
										
										
										
											2009-05-08 02:35:15 +01:00
										 |  |  |         hw_error("%s: bad slave address %i\n", __FUNCTION__, s->addr); | 
					
						
							| 
									
										
										
										
											2008-04-14 21:57:44 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-10 01:44:56 +01:00
										 |  |  | static void cbus_cycle(CBusPriv *s) | 
					
						
							| 
									
										
										
										
											2008-04-14 21:57:44 +00:00
										 |  |  | { | 
					
						
							|  |  |  |     switch (s->cycle) { | 
					
						
							|  |  |  |     case cbus_address: | 
					
						
							|  |  |  |         s->addr = (s->val >> 6) & 7; | 
					
						
							|  |  |  |         s->rw =   (s->val >> 5) & 1; | 
					
						
							|  |  |  |         s->reg =  (s->val >> 0) & 0x1f; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         s->cycle = cbus_value; | 
					
						
							|  |  |  |         s->bit = 15; | 
					
						
							|  |  |  |         s->dir = !s->rw; | 
					
						
							|  |  |  |         s->val = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (s->rw) | 
					
						
							|  |  |  |             cbus_io(s); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case cbus_value: | 
					
						
							|  |  |  |         if (!s->rw) | 
					
						
							|  |  |  |             cbus_io(s); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         s->cycle = cbus_address; | 
					
						
							|  |  |  |         s->bit = 8; | 
					
						
							|  |  |  |         s->dir = 1; | 
					
						
							|  |  |  |         s->val = 0; | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void cbus_clk(void *opaque, int line, int level) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2009-05-10 01:44:56 +01:00
										 |  |  |     CBusPriv *s = (CBusPriv *) opaque; | 
					
						
							| 
									
										
										
										
											2008-04-14 21:57:44 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (!s->sel && level && !s->clk) { | 
					
						
							|  |  |  |         if (s->dir) | 
					
						
							|  |  |  |             s->val |= s->dat << (s->bit --); | 
					
						
							|  |  |  |         else | 
					
						
							|  |  |  |             qemu_set_irq(s->dat_out, (s->val >> (s->bit --)) & 1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (s->bit < 0) | 
					
						
							|  |  |  |             cbus_cycle(s); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     s->clk = level; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void cbus_dat(void *opaque, int line, int level) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2009-05-10 01:44:56 +01:00
										 |  |  |     CBusPriv *s = (CBusPriv *) opaque; | 
					
						
							| 
									
										
										
										
											2008-04-14 21:57:44 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     s->dat = level; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void cbus_sel(void *opaque, int line, int level) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2009-05-10 01:44:56 +01:00
										 |  |  |     CBusPriv *s = (CBusPriv *) opaque; | 
					
						
							| 
									
										
										
										
											2008-04-14 21:57:44 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (!level) { | 
					
						
							|  |  |  |         s->dir = 1; | 
					
						
							|  |  |  |         s->bit = 8; | 
					
						
							|  |  |  |         s->val = 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     s->sel = level; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-10 01:44:56 +01:00
										 |  |  | CBus *cbus_init(qemu_irq dat) | 
					
						
							| 
									
										
										
										
											2008-04-14 21:57:44 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2011-08-20 22:09:37 -05:00
										 |  |  |     CBusPriv *s = (CBusPriv *) g_malloc0(sizeof(*s)); | 
					
						
							| 
									
										
										
										
											2008-04-14 21:57:44 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     s->dat_out = dat; | 
					
						
							| 
									
										
										
										
											2014-06-18 00:55:18 -07:00
										 |  |  |     s->cbus.clk = qemu_allocate_irq(cbus_clk, s, 0); | 
					
						
							|  |  |  |     s->cbus.dat = qemu_allocate_irq(cbus_dat, s, 0); | 
					
						
							|  |  |  |     s->cbus.sel = qemu_allocate_irq(cbus_sel, s, 0); | 
					
						
							| 
									
										
										
										
											2008-04-14 21:57:44 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     s->sel = 1; | 
					
						
							|  |  |  |     s->clk = 0; | 
					
						
							|  |  |  |     s->dat = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return &s->cbus; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-10 01:44:56 +01:00
										 |  |  | void cbus_attach(CBus *bus, void *slave_opaque) | 
					
						
							| 
									
										
										
										
											2008-04-14 21:57:44 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2009-05-10 01:44:56 +01:00
										 |  |  |     CBusSlave *slave = (CBusSlave *) slave_opaque; | 
					
						
							|  |  |  |     CBusPriv *s = (CBusPriv *) bus; | 
					
						
							| 
									
										
										
										
											2008-04-14 21:57:44 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     s->slave[slave->addr] = slave; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Retu/Vilma */ | 
					
						
							| 
									
										
										
										
											2009-05-10 01:44:56 +01:00
										 |  |  | typedef struct { | 
					
						
							| 
									
										
										
										
											2008-04-14 21:57:44 +00:00
										 |  |  |     uint16_t irqst; | 
					
						
							|  |  |  |     uint16_t irqen; | 
					
						
							|  |  |  |     uint16_t cc[2]; | 
					
						
							|  |  |  |     int channel; | 
					
						
							|  |  |  |     uint16_t result[16]; | 
					
						
							|  |  |  |     uint16_t sample; | 
					
						
							|  |  |  |     uint16_t status; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     struct { | 
					
						
							|  |  |  |         uint16_t cal; | 
					
						
							|  |  |  |     } rtc; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     int is_vilma; | 
					
						
							|  |  |  |     qemu_irq irq; | 
					
						
							| 
									
										
										
										
											2009-05-10 01:44:56 +01:00
										 |  |  |     CBusSlave cbus; | 
					
						
							|  |  |  | } CBusRetu; | 
					
						
							| 
									
										
										
										
											2008-04-14 21:57:44 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-10 01:44:56 +01:00
										 |  |  | static void retu_interrupt_update(CBusRetu *s) | 
					
						
							| 
									
										
										
										
											2008-04-14 21:57:44 +00:00
										 |  |  | { | 
					
						
							|  |  |  |     qemu_set_irq(s->irq, s->irqst & ~s->irqen); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define RETU_REG_ASICR		0x00	/* (RO) ASIC ID & revision */
 | 
					
						
							|  |  |  | #define RETU_REG_IDR		0x01	/* (T)  Interrupt ID */
 | 
					
						
							|  |  |  | #define RETU_REG_IMR		0x02	/* (RW) Interrupt mask */
 | 
					
						
							|  |  |  | #define RETU_REG_RTCDSR		0x03	/* (RW) RTC seconds register */
 | 
					
						
							|  |  |  | #define RETU_REG_RTCHMR		0x04	/* (RO) RTC hours and minutes reg */
 | 
					
						
							|  |  |  | #define RETU_REG_RTCHMAR	0x05	/* (RW) RTC hours and minutes set reg */
 | 
					
						
							|  |  |  | #define RETU_REG_RTCCALR	0x06	/* (RW) RTC calibration register */
 | 
					
						
							|  |  |  | #define RETU_REG_ADCR		0x08	/* (RW) ADC result register */
 | 
					
						
							|  |  |  | #define RETU_REG_ADCSCR		0x09	/* (RW) ADC sample control register */
 | 
					
						
							|  |  |  | #define RETU_REG_AFCR		0x0a	/* (RW) AFC register */
 | 
					
						
							|  |  |  | #define RETU_REG_ANTIFR		0x0b	/* (RW) AntiF register */
 | 
					
						
							|  |  |  | #define RETU_REG_CALIBR		0x0c	/* (RW) CalibR register*/
 | 
					
						
							|  |  |  | #define RETU_REG_CCR1		0x0d	/* (RW) Common control register 1 */
 | 
					
						
							|  |  |  | #define RETU_REG_CCR2		0x0e	/* (RW) Common control register 2 */
 | 
					
						
							|  |  |  | #define RETU_REG_RCTRL_CLR	0x0f	/* (T)  Regulator clear register */
 | 
					
						
							|  |  |  | #define RETU_REG_RCTRL_SET	0x10	/* (T)  Regulator set register */
 | 
					
						
							|  |  |  | #define RETU_REG_TXCR		0x11	/* (RW) TxC register */
 | 
					
						
							|  |  |  | #define RETU_REG_STATUS		0x16	/* (RO) Status register */
 | 
					
						
							|  |  |  | #define RETU_REG_WATCHDOG	0x17	/* (RW) Watchdog register */
 | 
					
						
							|  |  |  | #define RETU_REG_AUDTXR		0x18	/* (RW) Audio Codec Tx register */
 | 
					
						
							|  |  |  | #define RETU_REG_AUDPAR		0x19	/* (RW) AudioPA register */
 | 
					
						
							|  |  |  | #define RETU_REG_AUDRXR1	0x1a	/* (RW) Audio receive register 1 */
 | 
					
						
							|  |  |  | #define RETU_REG_AUDRXR2	0x1b	/* (RW) Audio receive register 2 */
 | 
					
						
							|  |  |  | #define RETU_REG_SGR1		0x1c	/* (RW) */
 | 
					
						
							|  |  |  | #define RETU_REG_SCR1		0x1d	/* (RW) */
 | 
					
						
							|  |  |  | #define RETU_REG_SGR2		0x1e	/* (RW) */
 | 
					
						
							|  |  |  | #define RETU_REG_SCR2		0x1f	/* (RW) */
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Retu Interrupt sources */ | 
					
						
							|  |  |  | enum { | 
					
						
							|  |  |  |     retu_int_pwr	= 0,	/* Power button */ | 
					
						
							|  |  |  |     retu_int_char	= 1,	/* Charger */ | 
					
						
							|  |  |  |     retu_int_rtcs	= 2,	/* Seconds */ | 
					
						
							|  |  |  |     retu_int_rtcm	= 3,	/* Minutes */ | 
					
						
							|  |  |  |     retu_int_rtcd	= 4,	/* Days */ | 
					
						
							|  |  |  |     retu_int_rtca	= 5,	/* Alarm */ | 
					
						
							|  |  |  |     retu_int_hook	= 6,	/* Hook */ | 
					
						
							|  |  |  |     retu_int_head	= 7,	/* Headset */ | 
					
						
							|  |  |  |     retu_int_adcs	= 8,	/* ADC sample */ | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Retu ADC channel wiring */ | 
					
						
							|  |  |  | enum { | 
					
						
							|  |  |  |     retu_adc_bsi	= 1,	/* BSI */ | 
					
						
							|  |  |  |     retu_adc_batt_temp	= 2,	/* Battery temperature */ | 
					
						
							|  |  |  |     retu_adc_chg_volt	= 3,	/* Charger voltage */ | 
					
						
							|  |  |  |     retu_adc_head_det	= 4,	/* Headset detection */ | 
					
						
							|  |  |  |     retu_adc_hook_det	= 5,	/* Hook detection */ | 
					
						
							|  |  |  |     retu_adc_rf_gp	= 6,	/* RF GP */ | 
					
						
							|  |  |  |     retu_adc_tx_det	= 7,	/* Wideband Tx detection */ | 
					
						
							|  |  |  |     retu_adc_batt_volt	= 8,	/* Battery voltage */ | 
					
						
							|  |  |  |     retu_adc_sens	= 10,	/* Light sensor */ | 
					
						
							|  |  |  |     retu_adc_sens_temp	= 11,	/* Light sensor temperature */ | 
					
						
							|  |  |  |     retu_adc_bbatt_volt	= 12,	/* Backup battery voltage */ | 
					
						
							|  |  |  |     retu_adc_self_temp	= 13,	/* RETU temperature */ | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-10 01:44:56 +01:00
										 |  |  | static inline uint16_t retu_read(CBusRetu *s, int reg) | 
					
						
							| 
									
										
										
										
											2008-04-14 21:57:44 +00:00
										 |  |  | { | 
					
						
							|  |  |  | #ifdef DEBUG
 | 
					
						
							|  |  |  |     printf("RETU read at %02x\n", reg); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     switch (reg) { | 
					
						
							|  |  |  |     case RETU_REG_ASICR: | 
					
						
							|  |  |  |         return 0x0215 | (s->is_vilma << 7); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case RETU_REG_IDR:	/* TODO: Or is this ffs(s->irqst)?  */ | 
					
						
							|  |  |  |         return s->irqst; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case RETU_REG_IMR: | 
					
						
							|  |  |  |         return s->irqen; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case RETU_REG_RTCDSR: | 
					
						
							|  |  |  |     case RETU_REG_RTCHMR: | 
					
						
							|  |  |  |     case RETU_REG_RTCHMAR: | 
					
						
							|  |  |  |         /* TODO */ | 
					
						
							|  |  |  |         return 0x0000; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case RETU_REG_RTCCALR: | 
					
						
							|  |  |  |         return s->rtc.cal; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case RETU_REG_ADCR: | 
					
						
							|  |  |  |         return (s->channel << 10) | s->result[s->channel]; | 
					
						
							|  |  |  |     case RETU_REG_ADCSCR: | 
					
						
							|  |  |  |         return s->sample; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case RETU_REG_AFCR: | 
					
						
							|  |  |  |     case RETU_REG_ANTIFR: | 
					
						
							|  |  |  |     case RETU_REG_CALIBR: | 
					
						
							|  |  |  |         /* TODO */ | 
					
						
							|  |  |  |         return 0x0000; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case RETU_REG_CCR1: | 
					
						
							|  |  |  |         return s->cc[0]; | 
					
						
							|  |  |  |     case RETU_REG_CCR2: | 
					
						
							|  |  |  |         return s->cc[1]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case RETU_REG_RCTRL_CLR: | 
					
						
							|  |  |  |     case RETU_REG_RCTRL_SET: | 
					
						
							|  |  |  |     case RETU_REG_TXCR: | 
					
						
							|  |  |  |         /* TODO */ | 
					
						
							|  |  |  |         return 0x0000; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case RETU_REG_STATUS: | 
					
						
							|  |  |  |         return s->status; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case RETU_REG_WATCHDOG: | 
					
						
							|  |  |  |     case RETU_REG_AUDTXR: | 
					
						
							|  |  |  |     case RETU_REG_AUDPAR: | 
					
						
							|  |  |  |     case RETU_REG_AUDRXR1: | 
					
						
							|  |  |  |     case RETU_REG_AUDRXR2: | 
					
						
							|  |  |  |     case RETU_REG_SGR1: | 
					
						
							|  |  |  |     case RETU_REG_SCR1: | 
					
						
							|  |  |  |     case RETU_REG_SGR2: | 
					
						
							|  |  |  |     case RETU_REG_SCR2: | 
					
						
							|  |  |  |         /* TODO */ | 
					
						
							|  |  |  |         return 0x0000; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     default: | 
					
						
							| 
									
										
										
										
											2009-05-08 02:35:15 +01:00
										 |  |  |         hw_error("%s: bad register %02x\n", __FUNCTION__, reg); | 
					
						
							| 
									
										
										
										
											2008-04-14 21:57:44 +00:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-10 01:44:56 +01:00
										 |  |  | static inline void retu_write(CBusRetu *s, int reg, uint16_t val) | 
					
						
							| 
									
										
										
										
											2008-04-14 21:57:44 +00:00
										 |  |  | { | 
					
						
							|  |  |  | #ifdef DEBUG
 | 
					
						
							|  |  |  |     printf("RETU write of %04x at %02x\n", val, reg); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     switch (reg) { | 
					
						
							|  |  |  |     case RETU_REG_IDR: | 
					
						
							|  |  |  |         s->irqst ^= val; | 
					
						
							|  |  |  |         retu_interrupt_update(s); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case RETU_REG_IMR: | 
					
						
							|  |  |  |         s->irqen = val; | 
					
						
							|  |  |  |         retu_interrupt_update(s); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case RETU_REG_RTCDSR: | 
					
						
							|  |  |  |     case RETU_REG_RTCHMAR: | 
					
						
							|  |  |  |         /* TODO */ | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case RETU_REG_RTCCALR: | 
					
						
							|  |  |  |         s->rtc.cal = val; | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case RETU_REG_ADCR: | 
					
						
							|  |  |  |         s->channel = (val >> 10) & 0xf; | 
					
						
							|  |  |  |         s->irqst |= 1 << retu_int_adcs; | 
					
						
							|  |  |  |         retu_interrupt_update(s); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case RETU_REG_ADCSCR: | 
					
						
							|  |  |  |         s->sample &= ~val; | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case RETU_REG_AFCR: | 
					
						
							|  |  |  |     case RETU_REG_ANTIFR: | 
					
						
							|  |  |  |     case RETU_REG_CALIBR: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case RETU_REG_CCR1: | 
					
						
							|  |  |  |         s->cc[0] = val; | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case RETU_REG_CCR2: | 
					
						
							|  |  |  |         s->cc[1] = val; | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case RETU_REG_RCTRL_CLR: | 
					
						
							|  |  |  |     case RETU_REG_RCTRL_SET: | 
					
						
							|  |  |  |         /* TODO */ | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case RETU_REG_WATCHDOG: | 
					
						
							|  |  |  |         if (val == 0 && (s->cc[0] & 2)) | 
					
						
							|  |  |  |             qemu_system_shutdown_request(); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case RETU_REG_TXCR: | 
					
						
							|  |  |  |     case RETU_REG_AUDTXR: | 
					
						
							|  |  |  |     case RETU_REG_AUDPAR: | 
					
						
							|  |  |  |     case RETU_REG_AUDRXR1: | 
					
						
							|  |  |  |     case RETU_REG_AUDRXR2: | 
					
						
							|  |  |  |     case RETU_REG_SGR1: | 
					
						
							|  |  |  |     case RETU_REG_SCR1: | 
					
						
							|  |  |  |     case RETU_REG_SGR2: | 
					
						
							|  |  |  |     case RETU_REG_SCR2: | 
					
						
							|  |  |  |         /* TODO */ | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     default: | 
					
						
							| 
									
										
										
										
											2009-05-08 02:35:15 +01:00
										 |  |  |         hw_error("%s: bad register %02x\n", __FUNCTION__, reg); | 
					
						
							| 
									
										
										
										
											2008-04-14 21:57:44 +00:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void retu_io(void *opaque, int rw, int reg, uint16_t *val) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2009-05-10 01:44:56 +01:00
										 |  |  |     CBusRetu *s = (CBusRetu *) opaque; | 
					
						
							| 
									
										
										
										
											2008-04-14 21:57:44 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (rw) | 
					
						
							|  |  |  |         *val = retu_read(s, reg); | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |         retu_write(s, reg, *val); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void *retu_init(qemu_irq irq, int vilma) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2011-08-20 22:09:37 -05:00
										 |  |  |     CBusRetu *s = (CBusRetu *) g_malloc0(sizeof(*s)); | 
					
						
							| 
									
										
										
										
											2008-04-14 21:57:44 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     s->irq = irq; | 
					
						
							|  |  |  |     s->irqen = 0xffff; | 
					
						
							|  |  |  |     s->irqst = 0x0000; | 
					
						
							|  |  |  |     s->status = 0x0020; | 
					
						
							|  |  |  |     s->is_vilma = !!vilma; | 
					
						
							|  |  |  |     s->rtc.cal = 0x01; | 
					
						
							|  |  |  |     s->result[retu_adc_bsi] = 0x3c2; | 
					
						
							|  |  |  |     s->result[retu_adc_batt_temp] = 0x0fc; | 
					
						
							|  |  |  |     s->result[retu_adc_chg_volt] = 0x165; | 
					
						
							|  |  |  |     s->result[retu_adc_head_det] = 123; | 
					
						
							|  |  |  |     s->result[retu_adc_hook_det] = 1023; | 
					
						
							|  |  |  |     s->result[retu_adc_rf_gp] = 0x11; | 
					
						
							|  |  |  |     s->result[retu_adc_tx_det] = 0x11; | 
					
						
							|  |  |  |     s->result[retu_adc_batt_volt] = 0x250; | 
					
						
							|  |  |  |     s->result[retu_adc_sens] = 2; | 
					
						
							|  |  |  |     s->result[retu_adc_sens_temp] = 0x11; | 
					
						
							|  |  |  |     s->result[retu_adc_bbatt_volt] = 0x3d0; | 
					
						
							|  |  |  |     s->result[retu_adc_self_temp] = 0x330; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     s->cbus.opaque = s; | 
					
						
							|  |  |  |     s->cbus.io = retu_io; | 
					
						
							|  |  |  |     s->cbus.addr = 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return &s->cbus; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void retu_key_event(void *retu, int state) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2009-05-10 01:44:56 +01:00
										 |  |  |     CBusSlave *slave = (CBusSlave *) retu; | 
					
						
							|  |  |  |     CBusRetu *s = (CBusRetu *) slave->opaque; | 
					
						
							| 
									
										
										
										
											2008-04-14 21:57:44 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     s->irqst |= 1 << retu_int_pwr; | 
					
						
							|  |  |  |     retu_interrupt_update(s); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (state) | 
					
						
							|  |  |  |         s->status &= ~(1 << 5); | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |         s->status |= 1 << 5; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-10-26 13:43:07 +00:00
										 |  |  | #if 0
 | 
					
						
							|  |  |  | static void retu_head_event(void *retu, int state) | 
					
						
							| 
									
										
										
										
											2008-04-14 21:57:44 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2009-05-10 01:44:56 +01:00
										 |  |  |     CBusSlave *slave = (CBusSlave *) retu; | 
					
						
							|  |  |  |     CBusRetu *s = (CBusRetu *) slave->opaque; | 
					
						
							| 
									
										
										
										
											2008-04-14 21:57:44 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if ((s->cc[0] & 0x500) == 0x500) {	/* TODO: Which bits? */ | 
					
						
							|  |  |  |         /* TODO: reissue the interrupt every 100ms or so.  */ | 
					
						
							|  |  |  |         s->irqst |= 1 << retu_int_head; | 
					
						
							|  |  |  |         retu_interrupt_update(s); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (state) | 
					
						
							|  |  |  |         s->result[retu_adc_head_det] = 50; | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |         s->result[retu_adc_head_det] = 123; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-10-26 13:43:07 +00:00
										 |  |  | static void retu_hook_event(void *retu, int state) | 
					
						
							| 
									
										
										
										
											2008-04-14 21:57:44 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2009-05-10 01:44:56 +01:00
										 |  |  |     CBusSlave *slave = (CBusSlave *) retu; | 
					
						
							|  |  |  |     CBusRetu *s = (CBusRetu *) slave->opaque; | 
					
						
							| 
									
										
										
										
											2008-04-14 21:57:44 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if ((s->cc[0] & 0x500) == 0x500) { | 
					
						
							|  |  |  |         /* TODO: reissue the interrupt every 100ms or so.  */ | 
					
						
							|  |  |  |         s->irqst |= 1 << retu_int_hook; | 
					
						
							|  |  |  |         retu_interrupt_update(s); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (state) | 
					
						
							|  |  |  |         s->result[retu_adc_hook_det] = 50; | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |         s->result[retu_adc_hook_det] = 123; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2008-10-26 13:43:07 +00:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2008-04-14 21:57:44 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | /* Tahvo/Betty */ | 
					
						
							| 
									
										
										
										
											2009-05-10 01:44:56 +01:00
										 |  |  | typedef struct { | 
					
						
							| 
									
										
										
										
											2008-04-14 21:57:44 +00:00
										 |  |  |     uint16_t irqst; | 
					
						
							|  |  |  |     uint16_t irqen; | 
					
						
							|  |  |  |     uint8_t charger; | 
					
						
							|  |  |  |     uint8_t backlight; | 
					
						
							|  |  |  |     uint16_t usbr; | 
					
						
							|  |  |  |     uint16_t power; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     int is_betty; | 
					
						
							|  |  |  |     qemu_irq irq; | 
					
						
							| 
									
										
										
										
											2009-05-10 01:44:56 +01:00
										 |  |  |     CBusSlave cbus; | 
					
						
							|  |  |  | } CBusTahvo; | 
					
						
							| 
									
										
										
										
											2008-04-14 21:57:44 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-10 01:44:56 +01:00
										 |  |  | static void tahvo_interrupt_update(CBusTahvo *s) | 
					
						
							| 
									
										
										
										
											2008-04-14 21:57:44 +00:00
										 |  |  | { | 
					
						
							|  |  |  |     qemu_set_irq(s->irq, s->irqst & ~s->irqen); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define TAHVO_REG_ASICR		0x00	/* (RO) ASIC ID & revision */
 | 
					
						
							|  |  |  | #define TAHVO_REG_IDR		0x01	/* (T)  Interrupt ID */
 | 
					
						
							|  |  |  | #define TAHVO_REG_IDSR		0x02	/* (RO) Interrupt status */
 | 
					
						
							|  |  |  | #define TAHVO_REG_IMR		0x03	/* (RW) Interrupt mask */
 | 
					
						
							|  |  |  | #define TAHVO_REG_CHAPWMR	0x04	/* (RW) Charger PWM */
 | 
					
						
							|  |  |  | #define TAHVO_REG_LEDPWMR	0x05	/* (RW) LED PWM */
 | 
					
						
							|  |  |  | #define TAHVO_REG_USBR		0x06	/* (RW) USB control */
 | 
					
						
							|  |  |  | #define TAHVO_REG_RCR		0x07	/* (RW) Some kind of power management */
 | 
					
						
							|  |  |  | #define TAHVO_REG_CCR1		0x08	/* (RW) Common control register 1 */
 | 
					
						
							|  |  |  | #define TAHVO_REG_CCR2		0x09	/* (RW) Common control register 2 */
 | 
					
						
							|  |  |  | #define TAHVO_REG_TESTR1	0x0a	/* (RW) Test register 1 */
 | 
					
						
							|  |  |  | #define TAHVO_REG_TESTR2	0x0b	/* (RW) Test register 2 */
 | 
					
						
							|  |  |  | #define TAHVO_REG_NOPR		0x0c	/* (RW) Number of periods */
 | 
					
						
							|  |  |  | #define TAHVO_REG_FRR		0x0d	/* (RO) FR */
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-10 01:44:56 +01:00
										 |  |  | static inline uint16_t tahvo_read(CBusTahvo *s, int reg) | 
					
						
							| 
									
										
										
										
											2008-04-14 21:57:44 +00:00
										 |  |  | { | 
					
						
							|  |  |  | #ifdef DEBUG
 | 
					
						
							|  |  |  |     printf("TAHVO read at %02x\n", reg); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     switch (reg) { | 
					
						
							|  |  |  |     case TAHVO_REG_ASICR: | 
					
						
							|  |  |  |         return 0x0021 | (s->is_betty ? 0x0b00 : 0x0300);	/* 22 in N810 */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case TAHVO_REG_IDR: | 
					
						
							|  |  |  |     case TAHVO_REG_IDSR:	/* XXX: what does this do?  */ | 
					
						
							|  |  |  |         return s->irqst; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case TAHVO_REG_IMR: | 
					
						
							|  |  |  |         return s->irqen; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case TAHVO_REG_CHAPWMR: | 
					
						
							|  |  |  |         return s->charger; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case TAHVO_REG_LEDPWMR: | 
					
						
							|  |  |  |         return s->backlight; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case TAHVO_REG_USBR: | 
					
						
							|  |  |  |         return s->usbr; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case TAHVO_REG_RCR: | 
					
						
							|  |  |  |         return s->power; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case TAHVO_REG_CCR1: | 
					
						
							|  |  |  |     case TAHVO_REG_CCR2: | 
					
						
							|  |  |  |     case TAHVO_REG_TESTR1: | 
					
						
							|  |  |  |     case TAHVO_REG_TESTR2: | 
					
						
							|  |  |  |     case TAHVO_REG_NOPR: | 
					
						
							|  |  |  |     case TAHVO_REG_FRR: | 
					
						
							|  |  |  |         return 0x0000; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     default: | 
					
						
							| 
									
										
										
										
											2009-05-08 02:35:15 +01:00
										 |  |  |         hw_error("%s: bad register %02x\n", __FUNCTION__, reg); | 
					
						
							| 
									
										
										
										
											2008-04-14 21:57:44 +00:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-10 01:44:56 +01:00
										 |  |  | static inline void tahvo_write(CBusTahvo *s, int reg, uint16_t val) | 
					
						
							| 
									
										
										
										
											2008-04-14 21:57:44 +00:00
										 |  |  | { | 
					
						
							|  |  |  | #ifdef DEBUG
 | 
					
						
							|  |  |  |     printf("TAHVO write of %04x at %02x\n", val, reg); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     switch (reg) { | 
					
						
							|  |  |  |     case TAHVO_REG_IDR: | 
					
						
							|  |  |  |         s->irqst ^= val; | 
					
						
							|  |  |  |         tahvo_interrupt_update(s); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case TAHVO_REG_IMR: | 
					
						
							|  |  |  |         s->irqen = val; | 
					
						
							|  |  |  |         tahvo_interrupt_update(s); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case TAHVO_REG_CHAPWMR: | 
					
						
							|  |  |  |         s->charger = val; | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case TAHVO_REG_LEDPWMR: | 
					
						
							|  |  |  |         if (s->backlight != (val & 0x7f)) { | 
					
						
							|  |  |  |             s->backlight = val & 0x7f; | 
					
						
							|  |  |  |             printf("%s: LCD backlight now at %i / 127\n", | 
					
						
							|  |  |  |                             __FUNCTION__, s->backlight); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case TAHVO_REG_USBR: | 
					
						
							|  |  |  |         s->usbr = val; | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case TAHVO_REG_RCR: | 
					
						
							|  |  |  |         s->power = val; | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case TAHVO_REG_CCR1: | 
					
						
							|  |  |  |     case TAHVO_REG_CCR2: | 
					
						
							|  |  |  |     case TAHVO_REG_TESTR1: | 
					
						
							|  |  |  |     case TAHVO_REG_TESTR2: | 
					
						
							|  |  |  |     case TAHVO_REG_NOPR: | 
					
						
							|  |  |  |     case TAHVO_REG_FRR: | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     default: | 
					
						
							| 
									
										
										
										
											2009-05-08 02:35:15 +01:00
										 |  |  |         hw_error("%s: bad register %02x\n", __FUNCTION__, reg); | 
					
						
							| 
									
										
										
										
											2008-04-14 21:57:44 +00:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void tahvo_io(void *opaque, int rw, int reg, uint16_t *val) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2009-05-10 01:44:56 +01:00
										 |  |  |     CBusTahvo *s = (CBusTahvo *) opaque; | 
					
						
							| 
									
										
										
										
											2008-04-14 21:57:44 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (rw) | 
					
						
							|  |  |  |         *val = tahvo_read(s, reg); | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |         tahvo_write(s, reg, *val); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void *tahvo_init(qemu_irq irq, int betty) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2011-08-20 22:09:37 -05:00
										 |  |  |     CBusTahvo *s = (CBusTahvo *) g_malloc0(sizeof(*s)); | 
					
						
							| 
									
										
										
										
											2008-04-14 21:57:44 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     s->irq = irq; | 
					
						
							|  |  |  |     s->irqen = 0xffff; | 
					
						
							|  |  |  |     s->irqst = 0x0000; | 
					
						
							|  |  |  |     s->is_betty = !!betty; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     s->cbus.opaque = s; | 
					
						
							|  |  |  |     s->cbus.io = tahvo_io; | 
					
						
							|  |  |  |     s->cbus.addr = 2; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return &s->cbus; | 
					
						
							|  |  |  | } |