static void lan9118_writel(void *opaque, hwaddr offset,
                           uint64_t val, unsigned size)
{
    lan9118_state *s = (lan9118_state *)opaque;
    offset &= 0xff;

    //DPRINTF("Write reg 0x%02x = 0x%08x\n", (int)offset, val);
    if (offset >= TX_DATA_FIFO_PORT_FIRST &&
        offset <= TX_DATA_FIFO_PORT_LAST) {
        /* TX FIFO */
        tx_fifo_push(s, val);
        return;
    }
    switch (offset) {
    case CSR_IRQ_CFG:
        /* TODO: Implement interrupt deassertion intervals.  */
        val &= (IRQ_EN | IRQ_POL | IRQ_TYPE);
        s->irq_cfg = (s->irq_cfg & IRQ_INT) | val;
        break;
    case CSR_INT_STS:
        s->int_sts &= ~val;
        break;
    case CSR_INT_EN:
        s->int_en = val & ~RESERVED_INT;
        s->int_sts |= val & SW_INT;
        break;
    case CSR_FIFO_INT:
        DPRINTF("FIFO INT levels %08x\n", val);
        s->fifo_int = val;
        break;
    case CSR_RX_CFG:
        if (val & 0x8000) {
            /* RX_DUMP */
            s->rx_fifo_used = 0;
            s->rx_status_fifo_used = 0;
            s->rx_packet_size_tail = s->rx_packet_size_head;
            s->rx_packet_size[s->rx_packet_size_head] = 0;
        }
        s->rx_cfg = val & 0xcfff1ff0;
        break;
    case CSR_TX_CFG:
        if (val & 0x8000) {
            s->tx_status_fifo_used = 0;
        }
        if (val & 0x4000) {
            s->txp->state = TX_IDLE;
            s->txp->fifo_used = 0;
            s->txp->cmd_a = 0xffffffff;
        }
        s->tx_cfg = val & 6;
        break;
    case CSR_HW_CFG:
        if (val & 1) {
            /* SRST */
            lan9118_reset(DEVICE(s));
        } else {
            s->hw_cfg = (val & 0x003f300) | (s->hw_cfg & 0x4);
        }
        break;
    case CSR_RX_DP_CTRL:
        if (val & 0x80000000) {
            /* Skip forward to next packet.  */
            s->rxp_pad = 0;
            s->rxp_offset = 0;
            if (s->rxp_size == 0) {
                /* Pop a word to start the next packet.  */
                rx_fifo_pop(s);
                s->rxp_pad = 0;
                s->rxp_offset = 0;
            }
            s->rx_fifo_head += s->rxp_size;
            if (s->rx_fifo_head >= s->rx_fifo_size) {
                s->rx_fifo_head -= s->rx_fifo_size;
            }
        }
        break;
    case CSR_PMT_CTRL:
        if (val & 0x400) {
            phy_reset(s);
        }
        s->pmt_ctrl &= ~0x34e;
        s->pmt_ctrl |= (val & 0x34e);
        break;
    case CSR_GPIO_CFG:
        /* Probably just enabling LEDs.  */
        s->gpio_cfg = val & 0x7777071f;
        break;
    case CSR_GPT_CFG:
        if ((s->gpt_cfg ^ val) & GPT_TIMER_EN) {
            ptimer_transaction_begin(s->timer);
            if (val & GPT_TIMER_EN) {
                ptimer_set_count(s->timer, val & 0xffff);
                ptimer_run(s->timer, 0);
            } else {
                ptimer_stop(s->timer);
                ptimer_set_count(s->timer, 0xffff);
            }
            ptimer_transaction_commit(s->timer);
        }
        s->gpt_cfg = val & (GPT_TIMER_EN | 0xffff);
        break;
    case CSR_WORD_SWAP:
        /* Ignored because we're in 32-bit mode.  */
        s->word_swap = val;
        break;
    case CSR_MAC_CSR_CMD:
        s->mac_cmd = val & 0x4000000f;
        if (val & 0x80000000) {
            if (val & 0x40000000) {
                s->mac_data = do_mac_read(s, val & 0xf);
                DPRINTF("MAC read %d = 0x%08x\n", val & 0xf, s->mac_data);
            } else {
                DPRINTF("MAC write %d = 0x%08x\n", val & 0xf, s->mac_data);
                do_mac_write(s, val & 0xf, s->mac_data);
            }
        }
        break;
    case CSR_MAC_CSR_DATA:
        s->mac_data = val;
        break;
    case CSR_AFC_CFG:
        s->afc_cfg = val & 0x00ffffff;
        break;
    case CSR_E2P_CMD:
        lan9118_eeprom_cmd(s, (val >> 28) & 7, val & 0x7f);
        break;
    case CSR_E2P_DATA:
        s->e2p_data = val & 0xff;
        break;

    default:
        qemu_log_mask(LOG_GUEST_ERROR, "lan9118_write: Bad reg 0x%x = %x\n",
                      (int)offset, (int)val);
        break;
    }
    lan9118_update(s);
}