tc6393xb.c 18.5 KB
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608
/*
 * Toshiba TC6393XB I/O Controller.
 * Found in Sharp Zaurus SL-6000 (tosa) or some
 * Toshiba e-Series PDAs.
 *
 * Most features are currently unsupported!!!
 *
 * This code is licensed under the GNU GPL v2.
 */
#include "hw.h"
#include "pxa.h"
#include "devices.h"
#include "flash.h"
#include "console.h"
#include "pixel_ops.h"

#define IRQ_TC6393_NAND		0
#define IRQ_TC6393_MMC		1
#define IRQ_TC6393_OHCI		2
#define IRQ_TC6393_SERIAL	3
#define IRQ_TC6393_FB		4

#define	TC6393XB_NR_IRQS	8

#define TC6393XB_GPIOS  16

#define SCR_REVID	0x08		/* b Revision ID	*/
#define SCR_ISR		0x50		/* b Interrupt Status	*/
#define SCR_IMR		0x52		/* b Interrupt Mask	*/
#define SCR_IRR		0x54		/* b Interrupt Routing	*/
#define SCR_GPER	0x60		/* w GP Enable		*/
#define SCR_GPI_SR(i)	(0x64 + (i))	/* b3 GPI Status	*/
#define SCR_GPI_IMR(i)	(0x68 + (i))	/* b3 GPI INT Mask	*/
#define SCR_GPI_EDER(i)	(0x6c + (i))	/* b3 GPI Edge Detect Enable */
#define SCR_GPI_LIR(i)	(0x70 + (i))	/* b3 GPI Level Invert	*/
#define SCR_GPO_DSR(i)	(0x78 + (i))	/* b3 GPO Data Set	*/
#define SCR_GPO_DOECR(i) (0x7c + (i))	/* b3 GPO Data OE Control */
#define SCR_GP_IARCR(i)	(0x80 + (i))	/* b3 GP Internal Active Register Control */
#define SCR_GP_IARLCR(i) (0x84 + (i))	/* b3 GP INTERNAL Active Register Level Control */
#define SCR_GPI_BCR(i)	(0x88 + (i))	/* b3 GPI Buffer Control */
#define SCR_GPA_IARCR	0x8c		/* w GPa Internal Active Register Control */
#define SCR_GPA_IARLCR	0x90		/* w GPa Internal Active Register Level Control */
#define SCR_GPA_BCR	0x94		/* w GPa Buffer Control */
#define SCR_CCR		0x98		/* w Clock Control	*/
#define SCR_PLL2CR	0x9a		/* w PLL2 Control	*/
#define SCR_PLL1CR	0x9c		/* l PLL1 Control	*/
#define SCR_DIARCR	0xa0		/* b Device Internal Active Register Control */
#define SCR_DBOCR	0xa1		/* b Device Buffer Off Control */
#define SCR_FER		0xe0		/* b Function Enable	*/
#define SCR_MCR		0xe4		/* w Mode Control	*/
#define SCR_CONFIG	0xfc		/* b Configuration Control */
#define SCR_DEBUG	0xff		/* b Debug		*/

#define NAND_CFG_COMMAND    0x04    /* w Command        */
#define NAND_CFG_BASE       0x10    /* l Control Base Address */
#define NAND_CFG_INTP       0x3d    /* b Interrupt Pin  */
#define NAND_CFG_INTE       0x48    /* b Int Enable     */
#define NAND_CFG_EC         0x4a    /* b Event Control  */
#define NAND_CFG_ICC        0x4c    /* b Internal Clock Control */
#define NAND_CFG_ECCC       0x5b    /* b ECC Control    */
#define NAND_CFG_NFTC       0x60    /* b NAND Flash Transaction Control */
#define NAND_CFG_NFM        0x61    /* b NAND Flash Monitor */
#define NAND_CFG_NFPSC      0x62    /* b NAND Flash Power Supply Control */
#define NAND_CFG_NFDC       0x63    /* b NAND Flash Detect Control */

#define NAND_DATA   0x00        /* l Data       */
#define NAND_MODE   0x04        /* b Mode       */
#define NAND_STATUS 0x05        /* b Status     */
#define NAND_ISR    0x06        /* b Interrupt Status */
#define NAND_IMR    0x07        /* b Interrupt Mask */

#define NAND_MODE_WP        0x80
#define NAND_MODE_CE        0x10
#define NAND_MODE_ALE       0x02
#define NAND_MODE_CLE       0x01
#define NAND_MODE_ECC_MASK  0x60
#define NAND_MODE_ECC_EN    0x20
#define NAND_MODE_ECC_READ  0x40
#define NAND_MODE_ECC_RST   0x60

struct TC6393xbState {
    qemu_irq irq;
    qemu_irq *sub_irqs;
    struct {
        uint8_t ISR;
        uint8_t IMR;
        uint8_t IRR;
        uint16_t GPER;
        uint8_t GPI_SR[3];
        uint8_t GPI_IMR[3];
        uint8_t GPI_EDER[3];
        uint8_t GPI_LIR[3];
        uint8_t GP_IARCR[3];
        uint8_t GP_IARLCR[3];
        uint8_t GPI_BCR[3];
        uint16_t GPA_IARCR;
        uint16_t GPA_IARLCR;
        uint16_t CCR;
        uint16_t PLL2CR;
        uint32_t PLL1CR;
        uint8_t DIARCR;
        uint8_t DBOCR;
        uint8_t FER;
        uint16_t MCR;
        uint8_t CONFIG;
        uint8_t DEBUG;
    } scr;
    uint32_t gpio_dir;
    uint32_t gpio_level;
    uint32_t prev_level;
    qemu_irq handler[TC6393XB_GPIOS];
    qemu_irq *gpio_in;

    struct {
        uint8_t mode;
        uint8_t isr;
        uint8_t imr;
    } nand;
    int nand_enable;
    uint32_t nand_phys;
    NANDFlashState *flash;
    ECCState ecc;

    DisplayState *ds;
    ram_addr_t vram_addr;
    uint16_t *vram_ptr;
    uint32_t scr_width, scr_height; /* in pixels */
    qemu_irq l3v;
    unsigned blank : 1,
             blanked : 1;
};

qemu_irq *tc6393xb_gpio_in_get(TC6393xbState *s)
{
    return s->gpio_in;
}

static void tc6393xb_gpio_set(void *opaque, int line, int level)
{
//    TC6393xbState *s = opaque;

    if (line > TC6393XB_GPIOS) {
        printf("%s: No GPIO pin %i\n", __FUNCTION__, line);
        return;
    }

    // FIXME: how does the chip reflect the GPIO input level change?
}

void tc6393xb_gpio_out_set(TC6393xbState *s, int line,
                    qemu_irq handler)
{
    if (line >= TC6393XB_GPIOS) {
        fprintf(stderr, "TC6393xb: no GPIO pin %d\n", line);
        return;
    }

    s->handler[line] = handler;
}

static void tc6393xb_gpio_handler_update(TC6393xbState *s)
{
    uint32_t level, diff;
    int bit;

    level = s->gpio_level & s->gpio_dir;

    for (diff = s->prev_level ^ level; diff; diff ^= 1 << bit) {
        bit = ffs(diff) - 1;
        qemu_set_irq(s->handler[bit], (level >> bit) & 1);
    }

    s->prev_level = level;
}

qemu_irq tc6393xb_l3v_get(TC6393xbState *s)
{
    return s->l3v;
}

static void tc6393xb_l3v(void *opaque, int line, int level)
{
    TC6393xbState *s = opaque;
    s->blank = !level;
    fprintf(stderr, "L3V: %d\n", level);
}

static void tc6393xb_sub_irq(void *opaque, int line, int level) {
    TC6393xbState *s = opaque;
    uint8_t isr = s->scr.ISR;
    if (level)
        isr |= 1 << line;
    else
        isr &= ~(1 << line);
    s->scr.ISR = isr;
    qemu_set_irq(s->irq, isr & s->scr.IMR);
}

#define SCR_REG_B(N)                            \
    case SCR_ ##N: return s->scr.N
#define SCR_REG_W(N)                            \
    case SCR_ ##N: return s->scr.N;             \
    case SCR_ ##N + 1: return s->scr.N >> 8;
#define SCR_REG_L(N)                            \
    case SCR_ ##N: return s->scr.N;             \
    case SCR_ ##N + 1: return s->scr.N >> 8;    \
    case SCR_ ##N + 2: return s->scr.N >> 16;   \
    case SCR_ ##N + 3: return s->scr.N >> 24;
#define SCR_REG_A(N)                            \
    case SCR_ ##N(0): return s->scr.N[0];       \
    case SCR_ ##N(1): return s->scr.N[1];       \
    case SCR_ ##N(2): return s->scr.N[2]

static uint32_t tc6393xb_scr_readb(TC6393xbState *s, target_phys_addr_t addr)
{
    switch (addr) {
        case SCR_REVID:
            return 3;
        case SCR_REVID+1:
            return 0;
        SCR_REG_B(ISR);
        SCR_REG_B(IMR);
        SCR_REG_B(IRR);
        SCR_REG_W(GPER);
        SCR_REG_A(GPI_SR);
        SCR_REG_A(GPI_IMR);
        SCR_REG_A(GPI_EDER);
        SCR_REG_A(GPI_LIR);
        case SCR_GPO_DSR(0):
        case SCR_GPO_DSR(1):
        case SCR_GPO_DSR(2):
            return (s->gpio_level >> ((addr - SCR_GPO_DSR(0)) * 8)) & 0xff;
        case SCR_GPO_DOECR(0):
        case SCR_GPO_DOECR(1):
        case SCR_GPO_DOECR(2):
            return (s->gpio_dir >> ((addr - SCR_GPO_DOECR(0)) * 8)) & 0xff;
        SCR_REG_A(GP_IARCR);
        SCR_REG_A(GP_IARLCR);
        SCR_REG_A(GPI_BCR);
        SCR_REG_W(GPA_IARCR);
        SCR_REG_W(GPA_IARLCR);
        SCR_REG_W(CCR);
        SCR_REG_W(PLL2CR);
        SCR_REG_L(PLL1CR);
        SCR_REG_B(DIARCR);
        SCR_REG_B(DBOCR);
        SCR_REG_B(FER);
        SCR_REG_W(MCR);
        SCR_REG_B(CONFIG);
        SCR_REG_B(DEBUG);
    }
    fprintf(stderr, "tc6393xb_scr: unhandled read at %08x\n", (uint32_t) addr);
    return 0;
}
#undef SCR_REG_B
#undef SCR_REG_W
#undef SCR_REG_L
#undef SCR_REG_A

#define SCR_REG_B(N)                                \
    case SCR_ ##N: s->scr.N = value; return;
#define SCR_REG_W(N)                                \
    case SCR_ ##N: s->scr.N = (s->scr.N & ~0xff) | (value & 0xff); return; \
    case SCR_ ##N + 1: s->scr.N = (s->scr.N & 0xff) | (value << 8); return
#define SCR_REG_L(N)                                \
    case SCR_ ##N: s->scr.N = (s->scr.N & ~0xff) | (value & 0xff); return;   \
    case SCR_ ##N + 1: s->scr.N = (s->scr.N & ~(0xff << 8)) | (value & (0xff << 8)); return;     \
    case SCR_ ##N + 2: s->scr.N = (s->scr.N & ~(0xff << 16)) | (value & (0xff << 16)); return;   \
    case SCR_ ##N + 3: s->scr.N = (s->scr.N & ~(0xff << 24)) | (value & (0xff << 24)); return;
#define SCR_REG_A(N)                                \
    case SCR_ ##N(0): s->scr.N[0] = value; return;   \
    case SCR_ ##N(1): s->scr.N[1] = value; return;   \
    case SCR_ ##N(2): s->scr.N[2] = value; return

static void tc6393xb_scr_writeb(TC6393xbState *s, target_phys_addr_t addr, uint32_t value)
{
    switch (addr) {
        SCR_REG_B(ISR);
        SCR_REG_B(IMR);
        SCR_REG_B(IRR);
        SCR_REG_W(GPER);
        SCR_REG_A(GPI_SR);
        SCR_REG_A(GPI_IMR);
        SCR_REG_A(GPI_EDER);
        SCR_REG_A(GPI_LIR);
        case SCR_GPO_DSR(0):
        case SCR_GPO_DSR(1):
        case SCR_GPO_DSR(2):
            s->gpio_level = (s->gpio_level & ~(0xff << ((addr - SCR_GPO_DSR(0))*8))) | ((value & 0xff) << ((addr - SCR_GPO_DSR(0))*8));
            tc6393xb_gpio_handler_update(s);
            return;
        case SCR_GPO_DOECR(0):
        case SCR_GPO_DOECR(1):
        case SCR_GPO_DOECR(2):
            s->gpio_dir = (s->gpio_dir & ~(0xff << ((addr - SCR_GPO_DOECR(0))*8))) | ((value & 0xff) << ((addr - SCR_GPO_DOECR(0))*8));
            tc6393xb_gpio_handler_update(s);
            return;
        SCR_REG_A(GP_IARCR);
        SCR_REG_A(GP_IARLCR);
        SCR_REG_A(GPI_BCR);
        SCR_REG_W(GPA_IARCR);
        SCR_REG_W(GPA_IARLCR);
        SCR_REG_W(CCR);
        SCR_REG_W(PLL2CR);
        SCR_REG_L(PLL1CR);
        SCR_REG_B(DIARCR);
        SCR_REG_B(DBOCR);
        SCR_REG_B(FER);
        SCR_REG_W(MCR);
        SCR_REG_B(CONFIG);
        SCR_REG_B(DEBUG);
    }
    fprintf(stderr, "tc6393xb_scr: unhandled write at %08x: %02x\n",
					(uint32_t) addr, value & 0xff);
}
#undef SCR_REG_B
#undef SCR_REG_W
#undef SCR_REG_L
#undef SCR_REG_A

static void tc6393xb_nand_irq(TC6393xbState *s) {
    qemu_set_irq(s->sub_irqs[IRQ_TC6393_NAND],
            (s->nand.imr & 0x80) && (s->nand.imr & s->nand.isr));
}

static uint32_t tc6393xb_nand_cfg_readb(TC6393xbState *s, target_phys_addr_t addr) {
    switch (addr) {
        case NAND_CFG_COMMAND:
            return s->nand_enable ? 2 : 0;
        case NAND_CFG_BASE:
        case NAND_CFG_BASE + 1:
        case NAND_CFG_BASE + 2:
        case NAND_CFG_BASE + 3:
            return s->nand_phys >> (addr - NAND_CFG_BASE);
    }
    fprintf(stderr, "tc6393xb_nand_cfg: unhandled read at %08x\n", (uint32_t) addr);
    return 0;
}
static void tc6393xb_nand_cfg_writeb(TC6393xbState *s, target_phys_addr_t addr, uint32_t value) {
    switch (addr) {
        case NAND_CFG_COMMAND:
            s->nand_enable = (value & 0x2);
            return;
        case NAND_CFG_BASE:
        case NAND_CFG_BASE + 1:
        case NAND_CFG_BASE + 2:
        case NAND_CFG_BASE + 3:
            s->nand_phys &= ~(0xff << ((addr - NAND_CFG_BASE) * 8));
            s->nand_phys |= (value & 0xff) << ((addr - NAND_CFG_BASE) * 8);
            return;
    }
    fprintf(stderr, "tc6393xb_nand_cfg: unhandled write at %08x: %02x\n",
					(uint32_t) addr, value & 0xff);
}

static uint32_t tc6393xb_nand_readb(TC6393xbState *s, target_phys_addr_t addr) {
    switch (addr) {
        case NAND_DATA + 0:
        case NAND_DATA + 1:
        case NAND_DATA + 2:
        case NAND_DATA + 3:
            return nand_getio(s->flash);
        case NAND_MODE:
            return s->nand.mode;
        case NAND_STATUS:
            return 0x14;
        case NAND_ISR:
            return s->nand.isr;
        case NAND_IMR:
            return s->nand.imr;
    }
    fprintf(stderr, "tc6393xb_nand: unhandled read at %08x\n", (uint32_t) addr);
    return 0;
}
static void tc6393xb_nand_writeb(TC6393xbState *s, target_phys_addr_t addr, uint32_t value) {
//    fprintf(stderr, "tc6393xb_nand: write at %08x: %02x\n",
//					(uint32_t) addr, value & 0xff);
    switch (addr) {
        case NAND_DATA + 0:
        case NAND_DATA + 1:
        case NAND_DATA + 2:
        case NAND_DATA + 3:
            nand_setio(s->flash, value);
            s->nand.isr &= 1;
            tc6393xb_nand_irq(s);
            return;
        case NAND_MODE:
            s->nand.mode = value;
            nand_setpins(s->flash,
                    value & NAND_MODE_CLE,
                    value & NAND_MODE_ALE,
                    !(value & NAND_MODE_CE),
                    value & NAND_MODE_WP,
                    0); // FIXME: gnd
            switch (value & NAND_MODE_ECC_MASK) {
                case NAND_MODE_ECC_RST:
                    ecc_reset(&s->ecc);
                    break;
                case NAND_MODE_ECC_READ:
                    // FIXME
                    break;
                case NAND_MODE_ECC_EN:
                    ecc_reset(&s->ecc);
            }
            return;
        case NAND_ISR:
            s->nand.isr = value;
            tc6393xb_nand_irq(s);
            return;
        case NAND_IMR:
            s->nand.imr = value;
            tc6393xb_nand_irq(s);
            return;
    }
    fprintf(stderr, "tc6393xb_nand: unhandled write at %08x: %02x\n",
					(uint32_t) addr, value & 0xff);
}

#define BITS 8
#include "tc6393xb_template.h"
#define BITS 15
#include "tc6393xb_template.h"
#define BITS 16
#include "tc6393xb_template.h"
#define BITS 24
#include "tc6393xb_template.h"
#define BITS 32
#include "tc6393xb_template.h"

static void tc6393xb_draw_graphic(TC6393xbState *s, int full_update)
{
    switch (ds_get_bits_per_pixel(s->ds)) {
        case 8:
            tc6393xb_draw_graphic8(s);
            break;
        case 15:
            tc6393xb_draw_graphic15(s);
            break;
        case 16:
            tc6393xb_draw_graphic16(s);
            break;
        case 24:
            tc6393xb_draw_graphic24(s);
            break;
        case 32:
            tc6393xb_draw_graphic32(s);
            break;
        default:
            printf("tc6393xb: unknown depth %d\n", ds_get_bits_per_pixel(s->ds));
            return;
    }

    dpy_update(s->ds, 0, 0, s->scr_width, s->scr_height);
}

static void tc6393xb_draw_blank(TC6393xbState *s, int full_update)
{
    int i, w;
    uint8_t *d;

    if (!full_update)
        return;

    w = s->scr_width * ((ds_get_bits_per_pixel(s->ds) + 7) >> 3);
    d = ds_get_data(s->ds);
    for(i = 0; i < s->scr_height; i++) {
        memset(d, 0, w);
        d += ds_get_linesize(s->ds);
    }

    dpy_update(s->ds, 0, 0, s->scr_width, s->scr_height);
}

static void tc6393xb_update_display(void *opaque)
{
    TC6393xbState *s = opaque;
    int full_update;

    if (s->scr_width == 0 || s->scr_height == 0)
        return;

    full_update = 0;
    if (s->blanked != s->blank) {
        s->blanked = s->blank;
        full_update = 1;
    }
    if (s->scr_width != ds_get_width(s->ds) || s->scr_height != ds_get_height(s->ds)) {
        qemu_console_resize(s->ds, s->scr_width, s->scr_height);
        full_update = 1;
    }
    if (s->blanked)
        tc6393xb_draw_blank(s, full_update);
    else
        tc6393xb_draw_graphic(s, full_update);
}


static uint32_t tc6393xb_readb(void *opaque, target_phys_addr_t addr) {
    TC6393xbState *s = opaque;

    switch (addr >> 8) {
        case 0:
            return tc6393xb_scr_readb(s, addr & 0xff);
        case 1:
            return tc6393xb_nand_cfg_readb(s, addr & 0xff);
    };

    if ((addr &~0xff) == s->nand_phys && s->nand_enable) {
//        return tc6393xb_nand_readb(s, addr & 0xff);
        uint8_t d = tc6393xb_nand_readb(s, addr & 0xff);
//        fprintf(stderr, "tc6393xb_nand: read at %08x: %02hhx\n", (uint32_t) addr, d);
        return d;
    }

//    fprintf(stderr, "tc6393xb: unhandled read at %08x\n", (uint32_t) addr);
    return 0;
}

static void tc6393xb_writeb(void *opaque, target_phys_addr_t addr, uint32_t value) {
    TC6393xbState *s = opaque;

    switch (addr >> 8) {
        case 0:
            tc6393xb_scr_writeb(s, addr & 0xff, value);
            return;
        case 1:
            tc6393xb_nand_cfg_writeb(s, addr & 0xff, value);
            return;
    };

    if ((addr &~0xff) == s->nand_phys && s->nand_enable)
        tc6393xb_nand_writeb(s, addr & 0xff, value);
    else
        fprintf(stderr, "tc6393xb: unhandled write at %08x: %02x\n",
					(uint32_t) addr, value & 0xff);
}

static uint32_t tc6393xb_readw(void *opaque, target_phys_addr_t addr)
{
    return (tc6393xb_readb(opaque, addr) & 0xff) |
        (tc6393xb_readb(opaque, addr + 1) << 8);
}

static uint32_t tc6393xb_readl(void *opaque, target_phys_addr_t addr)
{
    return (tc6393xb_readb(opaque, addr) & 0xff) |
        ((tc6393xb_readb(opaque, addr + 1) & 0xff) << 8) |
        ((tc6393xb_readb(opaque, addr + 2) & 0xff) << 16) |
        ((tc6393xb_readb(opaque, addr + 3) & 0xff) << 24);
}

static void tc6393xb_writew(void *opaque, target_phys_addr_t addr, uint32_t value)
{
    tc6393xb_writeb(opaque, addr, value);
    tc6393xb_writeb(opaque, addr + 1, value >> 8);
}

static void tc6393xb_writel(void *opaque, target_phys_addr_t addr, uint32_t value)
{
    tc6393xb_writeb(opaque, addr, value);
    tc6393xb_writeb(opaque, addr + 1, value >> 8);
    tc6393xb_writeb(opaque, addr + 2, value >> 16);
    tc6393xb_writeb(opaque, addr + 3, value >> 24);
}

TC6393xbState *tc6393xb_init(uint32_t base, qemu_irq irq)
{
    int iomemtype;
    TC6393xbState *s;
    CPUReadMemoryFunc *tc6393xb_readfn[] = {
        tc6393xb_readb,
        tc6393xb_readw,
        tc6393xb_readl,
    };
    CPUWriteMemoryFunc *tc6393xb_writefn[] = {
        tc6393xb_writeb,
        tc6393xb_writew,
        tc6393xb_writel,
    };

    s = (TC6393xbState *) qemu_mallocz(sizeof(TC6393xbState));
    s->irq = irq;
    s->gpio_in = qemu_allocate_irqs(tc6393xb_gpio_set, s, TC6393XB_GPIOS);

    s->l3v = *qemu_allocate_irqs(tc6393xb_l3v, s, 1);
    s->blanked = 1;

    s->sub_irqs = qemu_allocate_irqs(tc6393xb_sub_irq, s, TC6393XB_NR_IRQS);

    s->flash = nand_init(NAND_MFR_TOSHIBA, 0x76);

    iomemtype = cpu_register_io_memory(0, tc6393xb_readfn,
                    tc6393xb_writefn, s);
    cpu_register_physical_memory(base, 0x10000, iomemtype);

    s->vram_addr = qemu_ram_alloc(0x100000);
    s->vram_ptr = qemu_get_ram_ptr(s->vram_addr);
    cpu_register_physical_memory(base + 0x100000, 0x100000, s->vram_addr);
    s->scr_width = 480;
    s->scr_height = 640;
    s->ds = graphic_console_init(tc6393xb_update_display,
            NULL, /* invalidate */
            NULL, /* screen_dump */
            NULL, /* text_update */
            s);

    return s;
}