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