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