LAN9118 improvements

Implement LAN9118 general purpose timer and PHY interrupts. Fix global
interrupt status bit.

Signed-off-by: Paul Brook <paul@codesourcery.com>
This commit is contained in:
Paul Brook 2009-12-21 15:05:57 +00:00
parent 73b01960b4
commit 209bf9658a

View File

@ -40,8 +40,8 @@ do { fprintf(stderr, "lan9118: error: " fmt , ## __VA_ARGS__);} while (0)
#define CSR_TX_FIFO_INF 0x80
#define CSR_PMT_CTRL 0x84
#define CSR_GPIO_CFG 0x88
#define CSR_GPT_CFG 0x8c /* TODO */
#define CSR_GPT_CNT 0x90 /* TODO */
#define CSR_GPT_CFG 0x8c
#define CSR_GPT_CNT 0x90
#define CSR_WORD_SWAP 0x98
#define CSR_FREE_RUN 0x9c
#define CSR_RX_DROP 0xa0
@ -52,6 +52,7 @@ do { fprintf(stderr, "lan9118: error: " fmt , ## __VA_ARGS__);} while (0)
#define CSR_E2P_DATA 0xb4
/* IRQ_CFG */
#define IRQ_INT 0x00001000
#define IRQ_EN 0x00000100
#define IRQ_POL 0x00000010
#define IRQ_TYPE 0x00000001
@ -117,6 +118,16 @@ do { fprintf(stderr, "lan9118: error: " fmt , ## __VA_ARGS__);} while (0)
#define MAC_CR_RXEN 0x00000004
#define MAC_CR_RESERVED 0x7f404213
#define PHY_INT_ENERGYON 0x80
#define PHY_INT_AUTONEG_COMPLETE 0x40
#define PHY_INT_FAULT 0x20
#define PHY_INT_DOWN 0x10
#define PHY_INT_AUTONEG_LP 0x08
#define PHY_INT_PARFAULT 0x04
#define PHY_INT_AUTONEG_PAGE 0x02
#define GPT_TIMER_EN 0x20000000
enum tx_state {
TX_IDLE,
TX_B,
@ -141,6 +152,7 @@ typedef struct {
NICConf conf;
qemu_irq irq;
int mmio_index;
ptimer_state *timer;
uint32_t irq_cfg;
uint32_t int_sts;
@ -151,6 +163,7 @@ typedef struct {
uint32_t hw_cfg;
uint32_t pmt_ctrl;
uint32_t gpio_cfg;
uint32_t gpt_cfg;
uint32_t word_swap;
uint32_t free_timer_start;
uint32_t mac_cmd;
@ -169,6 +182,8 @@ typedef struct {
uint32_t phy_status;
uint32_t phy_control;
uint32_t phy_advertise;
uint32_t phy_int;
uint32_t phy_int_mask;
int eeprom_writable;
uint8_t eeprom[8];
@ -204,6 +219,11 @@ static void lan9118_update(lan9118_state *s)
/* TODO: Implement FIFO level IRQs. */
level = (s->int_sts & s->int_en) != 0;
if (level) {
s->irq_cfg |= IRQ_INT;
} else {
s->irq_cfg &= ~IRQ_INT;
}
if ((s->irq_cfg & IRQ_EN) == 0) {
level = 0;
}
@ -231,14 +251,28 @@ static void lan9118_reload_eeprom(lan9118_state *s)
lan9118_mac_changed(s);
}
static void phy_update_irq(lan9118_state *s)
{
if (s->phy_int & s->phy_int_mask) {
s->int_sts |= PHY_INT;
} else {
s->int_sts &= ~PHY_INT;
}
lan9118_update(s);
}
static void phy_update_link(lan9118_state *s)
{
/* Autonegotiation status mirrors link status. */
if (s->nic->nc.link_down) {
s->phy_status &= ~0x0024;
s->phy_int |= PHY_INT_DOWN;
} else {
s->phy_status |= 0x0024;
s->phy_int |= PHY_INT_ENERGYON;
s->phy_int |= PHY_INT_AUTONEG_COMPLETE;
}
phy_update_irq(s);
}
static void lan9118_set_link(VLANClientState *nc)
@ -248,9 +282,11 @@ static void lan9118_set_link(VLANClientState *nc)
static void phy_reset(lan9118_state *s)
{
s->phy_status = 0x7805;
s->phy_status = 0x7809;
s->phy_control = 0x3000;
s->phy_advertise = 0x01e1;
s->phy_int_mask = 0;
s->phy_int = 0;
phy_update_link(s);
}
@ -292,6 +328,10 @@ static void lan9118_reset(DeviceState *d)
s->e2p_data = 0;
s->free_timer_start = qemu_get_clock(vm_clock) / 40;
ptimer_stop(s->timer);
ptimer_set_count(s->timer, 0xffff);
s->gpt_cfg = 0xffff;
s->mac_cr = MAC_CR_PRMS;
s->mac_hashh = 0;
s->mac_hashl = 0;
@ -640,6 +680,8 @@ static void tx_fifo_push(lan9118_state *s, uint32_t val)
static uint32_t do_phy_read(lan9118_state *s, int reg)
{
uint32_t val;
switch (reg) {
case 0: /* Basic Control */
return s->phy_control;
@ -656,6 +698,13 @@ static uint32_t do_phy_read(lan9118_state *s, int reg)
case 6: /* Auto-neg Expansion */
return 1;
/* TODO 17, 18, 27, 29, 30, 31 */
case 29: /* Interrupt source. */
val = s->phy_int;
s->phy_int = 0;
phy_update_irq(s);
return val;
case 30: /* Interrupt mask */
return s->phy_int_mask;
default:
BADF("PHY read reg %d\n", reg);
return 0;
@ -679,7 +728,11 @@ static void do_phy_write(lan9118_state *s, int reg, uint32_t val)
case 4: /* Auto-neg advertisment */
s->phy_advertise = (val & 0x2d7f) | 0x80;
break;
/* TODO 17, 18, 27, 29, 30, 31 */
/* TODO 17, 18, 27, 31 */
case 30: /* Interrupt mask */
s->phy_int_mask = val & 0xff;
phy_update_irq(s);
break;
default:
BADF("PHY write reg %d = 0x%04x\n", reg, val);
}
@ -820,6 +873,15 @@ static void lan9118_eeprom_cmd(lan9118_state *s, int cmd, int addr)
}
}
static void lan9118_tick(void *opaque)
{
lan9118_state *s = (lan9118_state *)opaque;
if (s->int_en & GPT_INT) {
s->int_sts |= GPT_INT;
}
lan9118_update(s);
}
static void lan9118_writel(void *opaque, target_phys_addr_t offset,
uint32_t val)
{
@ -835,7 +897,7 @@ static void lan9118_writel(void *opaque, target_phys_addr_t offset,
switch (offset) {
case CSR_IRQ_CFG:
/* TODO: Implement interrupt deassertion intervals. */
s->irq_cfg = (val & IRQ_EN);
s->irq_cfg = (s->irq_cfg & IRQ_INT) | (val & IRQ_EN);
break;
case CSR_INT_STS:
s->int_sts &= ~val;
@ -905,6 +967,18 @@ static void lan9118_writel(void *opaque, target_phys_addr_t offset,
/* Probably just enabling LEDs. */
s->gpio_cfg = val & 0x7777071f;
break;
case CSR_GPT_CFG:
if ((s->gpt_cfg ^ val) & GPT_TIMER_EN) {
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);
}
}
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;
@ -988,6 +1062,10 @@ static uint32_t lan9118_readl(void *opaque, target_phys_addr_t offset)
return s->pmt_ctrl;
case CSR_GPIO_CFG:
return s->gpio_cfg;
case CSR_GPT_CFG:
return s->gpt_cfg;
case CSR_GPT_CNT:
return ptimer_get_count(s->timer);
case CSR_WORD_SWAP:
return s->word_swap;
case CSR_FREE_RUN:
@ -1041,6 +1119,7 @@ static NetClientInfo net_lan9118_info = {
static int lan9118_init1(SysBusDevice *dev)
{
lan9118_state *s = FROM_SYSBUS(lan9118_state, dev);
QEMUBH *bh;
int i;
s->mmio_index = cpu_register_io_memory(lan9118_readfn,
@ -1059,6 +1138,11 @@ static int lan9118_init1(SysBusDevice *dev)
s->pmt_ctrl = 1;
s->txp = &s->tx_packet;
bh = qemu_bh_new(lan9118_tick, s);
s->timer = ptimer_init(bh);
ptimer_set_freq(s->timer, 10000);
ptimer_set_limit(s->timer, 0xffff, 1);
/* ??? Save/restore. */
return 0;
}