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